DataAnnotations - Validation not happening - asp.net-mvc

validation is working normal on local box, dev site but it is not happening on the staging and production sites. Both client side and server side validation is not happening. Both staging and production are load balanced but use sticky connection due to some other functional requirements.
I have checked the bin folder on all environments and i see the following two dlls there.
DataAnnotationsExtensions.ClientValidation.dll
DataAnnotationsExtensions.dll
On the server side, following should fail but it is not happening.
!TryValidateModel(model) || !ModelState.IsValid
This site use windows authentication.
Web.config
<appSettings file="Configs\AppSettings_LocalHost.config">
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
For testing purposes, i am not using bundles at this time. For the bundles, i even tested it with following
<location path="~/Content">
<system.web>
<authorization>
<allow users ="*" />
</authorization>
</system.web>
</location>
<location path="~/bundles">
<system.web>
<authorization>
<allow users ="*" />
</authorization>
</system.web>
</location>
<location path="~/Scripts">
<system.web>
<authorization>
<allow users ="*" />
</authorization>
</system.web>
</location>
And i have the following JS files referenced as well
<script src="/NetSite/Scripts/Core/jquery.validate.min.js?v=1.12" type="text/javascript"></script>
<script src="/NetSite/Scripts/Core/jquery.validate.unobtrusive.min.js?v=1.12" type="text/javascript"></script>
<script src="/NetSite/Scripts/Custom/Validators.js?v=1.12" type="text/javascript"></script>
The app is MVC 5 and all has been added via the NuGet package. I don't have the MVC installed on the server. I have another MVC 5 app on these servers and validation is happening just fine.
And here is the form tag, the second working app uses the same form tag.
using (Html.BeginForm(ActionNames.Index, ControllerNames.Rankings, new { Area = AreaNames.MemberToolsReports }, FormMethod.Post, new { id = "RankingsSearchForm" }))
On the old staging and production boxes, validation was working but then we had MVC 3 installed on it.
Update - Controller Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Project.BusinessEntities;
using Project.Common.Constants;
using Project.MvcBase;
using Project.Resources;
using Project.ServiceInterfaces;
using Project.ViewModels;
using Project.ViewModels.MemberToolReports;
using Microsoft.Practices.Unity;
using Project.Helpers.Helpers;
using Project.Helpers.IO;
namespace Project.Site.Areas.MemberToolsReports.Controllers
{
public class RankingsController : BaseController
{
#region PROPERTIES
[Dependency]
public IGeographyService GeographyServiceInstance { get; set; }
[Dependency]
public IRankingsService RankingsServiceInstance { get; set; }
[Dependency]
public IUtilityService UtilityServiceInstance { get; set; }
#endregion
#region ACTIONS
public ActionResult Index()
{
var states = getting states here
var key = String.Empty;
var search = new RankingSearch { Key = key };
var model = new RankingSearchViewModel { Search = search, StatesList = states };
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(RankingSearchViewModel model)
{
var errorModel = new ContentShowError { IsError = true };
var resultModel = new RankingsSearchResultsViewModel();
try
{
//TODO: remove extra code once data annotations issue is fixed on staging and prod
if (!Request.IsAjaxRequest())
{
errorModel.Message = base.GetDisplayMessage(ProcessingMessagesEnum.ErrorServicingRequest);
}
else if (!TryValidateModel(model) || !ModelState.IsValid)
{
errorModel.Message = base.GetDisplayMessage(ProcessingMessagesEnum.ErrorProcessingRequest);
}
else if (String.IsNullOrWhiteSpace(model.Search.Key) &&
String.IsNullOrWhiteSpace(model.Search.Institution) &&
String.IsNullOrWhiteSpace(model.Search.State))
{
errorModel.Message = base.GetDisplayMessage(ProcessingMessagesEnum.NoCriteriaSpecified);
}
else
{
//default - debug code
errorModel.Message = base.GetDisplayMessage(ProcessingMessagesEnum.ErrorNoDataFound);
var results = RankingsServiceInstance.SearchRanking(model.Search);
if (results != null && results.Count > 0)
{
errorModel.IsError = false;
errorModel.Message = String.Empty;
//update result model
resultModel.Rankings = results;
}
}
}
catch (Exception ex)
{
errorModel.Message = base.GetDisplayMessage(ProcessingMessagesEnum.ErrorProcessingRequest);
base.LogException(ex);
}
ActionResult result = null;
result = errorModel.IsError ? PartialView(ViewNames.ErrorControl, errorModel) : PartialView(ViewNames.SearchResultsControl, resultModel);
return result;
}
#endregion
}
}
Update 2 - HTML Difference
Looks like validation attributes are not even making it to the html, as if the site doesn't even know that we are using validation. Right now, both, dev and staging sites have the same code.
Staging site
<input autofocus="autofocus" class="clearSearchFields" id="Search_Key" maxlength="6" name="Search.Key" size="6" type="text" value="" /><br />
Working dev site
<input autofocus="autofocus" class="clearSearchFields" data-val="true" data-val-length="Key must be 6 characters long" data-val-length-max="6" data-val-length-min="6" data-val-regex="Only alphanumeric (A-Z a-z 0-9) values are allowed" data-val-regex-pattern="[A-Za-z0-9]*" id="Search_Key" maxlength="6" name="Search.Key" size="6" type="text" value="" /><br />
<span class="field-validation-valid" data-valmsg-for="Search.Key" data-valmsg-replace="true"></span>

Since you have had another mvc 5 app on that machine that which runs fine and you don't have MVC installed it would appear that something isn't being deployed correctly. Most likely something there is some assembly needed in the MVC package that you don't have.
Is there a reason you can't install MVC on the server? There are stand alone packages available for that. That should add everything to the GAC that you need.
If you can't install MVC I would look at the bin of your working MVC 5 app. Does it appears to have more .Net assemblies than your new application? If so then someone probably included all the missing MVC assemblies in it. You could try copying all the assemblies over from the working mvc app, just make sure you don't override. That should show you any assembly that you are missing.

Since some of the posters do not read the fully question and comments and try to answer, i am moving my last two comments from the question thread as an answer. My issue is fixed.
I moved the web.config from my local machine over to staging and prod and validation started working. I have checked the old web.config and this new working folder web.config and there are no differences. Even though it is working, i am happy but i am now confused at the same time.
Looks like the ASP.NET temp file was an issue in this case. When i updated the web.config manually, the temp file got updated as well, which fixed the issue for me.

Related

Using dot in URL

I have the following route in RouteConfig.cs:
routes.MapRoute(
"MyLegacyRoute",
"Content/bootstrap.css",
new { controller = "Legacy", action = "GetLegacyUrl", legacyUrl = "someUrl" });
~/Content/bootstrap.css exists and rather than display its content when I navigate to http://localhost:27541/Content/bootstrap.css, I want to hit the GetLegacyUrl action in the Legacy controller.
I have added this to Web.config:
<system.webServer>
<handlers>
<add name="UrlRoutingHandler" path="/Content/*" verb="GET" type="System.Web.Routing.UrlRoutingHandler" />
...
and
<system.web>
<httpRuntime targetFramework="4.5" relaxedUrlToFileSystemMapping="true"/>
...
However when I access http://localhost:27541/Content/bootstrap.css I get this error:
Server Error in '/' Application.
Cannot create an abstract class.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.MissingMethodException: Cannot create an abstract class.
My controller looks like this:
public class LegacyController : Controller
{
public ActionResult GetLegacyUrl(string legacyUrl)
{
return View((object)legacyUrl);
}
}
My view (GetLegacyUrl.cshtml) looks like this:
#model string
#{
ViewBag.Title = "GetLegacyUrl";
Layout = null;
}
<h2>GetLegacyUrl</h2>
The URL requested was #Model
I am trying to do this just so I can learn more about routing.
What can I do to successfully use this route?
I got it to work by putting this in RouteConfig.cs:
routes.RouteExistingFiles = true;
And by making the preCondition value an empty string:
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
in this file: C:\Users\user.name\Documents\IISExpress\config\applicationhost.config
I found the answer on pg 99/115 chapter 16 in Adam Freeman's Pro ASP.NET MVC5 book.
I also removed the handler I added in the Web.config and removed the relaxedUrlToFileSystemMapping="true" since that doesn't work in MVC5.

Why might I not be getting any results from MiniProfiler?

I can't seem to get any results from MiniProfiler.
I can see the XHR POST to /mini-profiler-resources/results returning a 404. The chrome request inspector gives me a standard server 404 page.
A GET on the same URL also returns a 404, but there is a message saying that "no results were found for the specified id" (I did add the id in the querystring).
When I look at /mini-profiler-resources/results-index it just gives me an empty table with the field headings in - name, started, sql, duration, etc.
I've tried a few things - details below - at this stage I am at a loss as to what I can try next. Any pointers or suggestions for debugging this problem would be much appreciated.
Installed Packages:
MiniProfiler (v3.2.0.157)
MiniProfiler.EF6 (v3.0.11)
MiniProfiler.MVC4 (v3.0.11)
Where MVC4 also caters for MVC5. Which this project is.
Relevant Code:
protected void Application_Start()
{
MiniProfilerEF6.Initialize();
MiniProfiler.Settings.Results_List_Authorize = IsUserAllowedToSeeMiniProfilerUI;
MiniProfiler.Settings.MaxJsonResponseSize = int.MaxValue;
Database.SetInitializer<ApplicationDbContext>(null);
GlobalFilters.Filters.Add(new ProfilingActionFilter());
var copy = ViewEngines.Engines.ToList();
ViewEngines.Engines.Clear();
foreach (var item in copy)
{
ViewEngines.Engines.Add(new ProfilingViewEngine(item));
}
}
protected void Application_BeginRequest(Object source, EventArgs e)
{
if (Request.IsLocal)
{
// this IS being hit
MiniProfiler.Start();
}
}
protected void Applicaton_EndRequest()
{
MiniProfiler.Stop(discardResults: false);
}
private bool IsUserAllowedToSeeMiniProfilerUI(HttpRequest httpRequest)
{
return true;
}
In HomeController.cs:
[AllowAnonymous]
public ActionResult Index()
{
var profiler = MiniProfiler.Current;
using (profiler.Step("Doing complex stuff"))
{
using (profiler.Step("Step A"))
{
ViewBag.Title = "Home Page";
}
using (profiler.Step("Step B"))
{
Thread.Sleep(250);
}
}
return View();
}
And in MasterLayout.cshtml:
#using StackExchange.Profiling;
...
#MiniProfiler.RenderIncludes()
I've Tried:
I have set discardResults to false, like this:
protected void Applicaton_EndRequest()
{
MiniProfiler.Stop(discardResults: false);
}
I can confirm that MiniProfiler.Start() IS getting hit when the page loads.
I can also confirm that the mini-profiler-resources/ route IS being found (using Haack's Route Debugger)
I have the following item in the <handlers> section of web.config, and it is in the correct section (e.g. this guy mistakenly put it in the ELMAH config ).
<add name="MiniProfiler" path="mini-profiler-resources/*" verb="*" type="System.Web.Routing.UrlRoutingModule" resourceType="Unspecified" preCondition="integratedMode" />
I have set all my output caching to 1 second.
I was using a custom applicationhost.config file to test on a custom url.
I tried removing the custom url bindings and just using the standard localhost:51347.
I also tried putting the snippet below into applicationhost.config instead of the standard web.config.
<add name="MiniProfiler" path="mini-profiler-resources/*" verb="*" type="System.Web.Routing.UrlRoutingModule" resourceType="Unspecified" preCondition="integratedMode" />
In applicationhost.config I tried changing
<section name="handlers" overrideModeDefault="Deny" />
to
<section name="handlers" overrideModeDefault="Allow" />
I've added the following into application_start:
MiniProfiler.Settings.MaxJsonResponseSize = int.MaxValue;
As recommended in this answer, I have tried uninstalling all related packages and reinstalling them in this order:
MiniProfiler
MiniProfiler.MVC4
MiniProfiler.EF6
Update
9 days on, the bounty expired and still no joy :(
If anybody reading this in the future has ideas / suggestions, I'd really like to hear them. MiniProfiler is such a great tool and I'm disappointed that I haven't been able to get it working on this occasion.
If I do find the answer myself, I'll post it.
After running into the same issue I found the answer here which worked fine.
http://www.mukeshkumar.net/articles/mvc/performance-test-with-miniprofiler-in-asp-net-mvc
If you get an error after running application with MiniProfiler.Mvc4 or MiniProfiler.Mvc3 which state “/mini-profiler-resources/includes.js 404 not found” then simply add the following line of code in Web.Config inside web server section.
<system.webServer>
<handlers>
<add name="MiniProfiler" path="mini-profiler-resources/*"
verb="*" type="System.Web.Routing.UrlRoutingModule"
resourceType="Unspecified" preCondition="integratedMode" />
</handlers>
</system.webServer>

forms authentication gives a too long query string [duplicate]

This question already has an answer here:
ASP.NET MVC 5 : Endless redirect to the login page using the site template
(1 answer)
Closed 8 years ago.
Im trying to make a (temporary) login storing the users in my web.config file.
After adding deny to the web.config file it gives me this error
HTTP Error 404.15 - Not Found
The request filtering module is configured to deny a request where the query string is too long.
The url looks like this
http://localhost/Account/Login?ReturnUrl=%2FAccount%2FLogin%3FReturnUrl%3D%252FAccount%252FLogin%253FReturnUrl%253D%25252FAccount%25252FLogin%25253FReturnUrl%25253D%2525252FAccount%2525252FLogin%2525253FReturnUrl%2525253D%252525252FAccount%252525252FLogin%252525253FReturnUrl%252525253D%25252525252FAccount%25252525252FLogin%25252525253FReturnUrl%25252525253D%2525252525252FAccount%2525252525252FLogin%2525252525253FReturnUrl%2525252525253D%252525252525252FAccount%252525252525252FLogin%252525252525253FReturnUrl%252525252525253D%25252525252525252FAccount%25252525252525252FLogin%25252525252525253FReturnUrl%25252525252525253D%2525252525252525252FAccount%2525252525252525252FLogin%2525252525252525253FReturnUrl%2525252525252525253D%252525252525252525252FAccount%252525252525252525252FLogin%252525252525252525253FReturnUrl%252525252525252525253D%25252525252525252525252FAccount%25252525252525252525252FLogin%25252525252525252525253FReturnUrl%25252525252525252525253D%2525252525252525252525252FAccount%2525252525252525252525252FLogin%2525252525252525252525253FReturnUrl%2525252525252525252525253D%252525252525252525252525252FAccount%252525252525252525252525252FLogin%252525252525252525252525253FReturnUrl%252525252525252525252525253D%25252525252525252525252525252FAccount%25252525252525252525252525252FLogin%25252525252525252525252525253FReturnUrl%25252525252525252525252525253D%2525252525252525252525252525252FAccount%2525252525252525252525252525252FLogin%2525252525252525252525252525253FReturnUrl%2525252525252525252525252525253D%252525252525252525252525252525252FAccount%252525252525252525252525252525252FLogin%252525252525252525252525252525253FReturnUrl%252525252525252525252525252525253D%25252525252525252525252525252525252FAccount%25252525252525252525252525252525252FLogin%25252525252525252525252525252525253FReturnUrl%25252525252525252525252525252525253D%2525252525252525252525252525252525252FAccount%2525252525252525252525252525252525252FLogin%2525252525252525252525252525252525253FReturnUrl%2525252525252525252525252525252525253D%252525252525252525252525252525252525252FAccount%252525252525252525252525252525252525252FLogin%252525252525252525252525252525252525253FReturnUrl%252525252525252525252525252525252525253D%25252525252525252525252525252525252525252F
(without deny it sets the cookie but i can still access all the pages)
This is how it looks in my web.config
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" name=".ASPXAUTH" slidingExpiration="true" timeout="1440" path="/" defaultUrl="~/">
<credentials passwordFormat="Clear">
<user name="matchUser80" password="123Match789"/>
</credentials>
</forms>
</authentication>
<authorization>
<deny users="?" />
</authorization>
And my controller
[HttpPost]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
if (FormsAuthentication.Authenticate(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
FormsAuthentication.RedirectFromLoginPage(model.UserName, false);
if (returnUrl != null)
{
return Redirect(returnUrl);
}
return View();
}
ModelState.AddModelError(string.Empty, "Wrong username or password");
return View(model);
}
I'm using MVC 5.
You should use attributes instead of web.config configuration to authorize your mvc application. Web config configuration should be used only with web form applications.
Decorate your Login action (both get and post version) with [AllowAnonymous] attribute.
User [Authorize] attribute for other controllers.
Read this article to see how to secure your mvc application.
Update
I reproduced your problem locally with default mvc project and i had this in my web.config:
<system.webServer>
<modules>
<remove name="FormsAuthentication" />
</modules>
</system.webServer>
Everything started working after i commented the <remove name="FormsAuthentication" /> part

Specifying Roles in web.config of an asp.net MVC application

I am creating an MVC application with forms auth. I am authenticating against active directory and so have created a custom RoleProvider. My application is only concerned with a small set of roles which up until now I have been defining in the appSettings section of my web.config:
<appSettings>
<add key="DirectorRole" value="Domain\Directors" />
<add key="ManagementRole" value="Domain\Managers" />
...
</appSettings>
However I have run into a couple of problems with this approach:
I cannot reference these setting in my contoller data annotations: [Authorize(Roles = ConfigurationManager.AppSettings["DirectorRole"])] as it wont compile so I have to specify the name of the group again: [Authorize(Roles = "Domain\\Directors")].
In my web.config, I would like to specify the groupsToUse for my role provider and just reference a pre-existing list, rather than maintain two seperate lists of the same set of roles.
It seems that there must be a better/reusable way to define the roles in the web.config, can someone point me in the right direction please?
I would prefer using a custom authorize attribute. Like this one.
public class MyAuthorizeAttribute : AuthorizeAttribute {
public MyAuthorizeAttribute(params string[] roleKeys) {
List<string> roles = new List<string>(roleKeys.Length);
//foreach(var roleKey in roleKeys) {
//roles.Add(ConfigurationManager.AppSettings["DirectorRole"]);
//}
var allRoles = (NameValueCollection)ConfigurationManager.GetSection("roles");
foreach(var roleKey in roleKeys) {
roles.Add(allRoles[roleKey]);
}
this.Roles = string.Join(",", roles);
}
}
In your controller, use:
[MyAuthorize("DirectorRole")]
In your web.config
<configSections>
<section
name="roles"
type="System.Configuration.NameValueFileSectionHandler,System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</configSections>
<roles>
<add key="DirectorRole" value="Domain\Directors" />
<add key="ManagementRole" value="Domain\Managers" />
</roles>
I hope this will solve your first problem just fine. And twiking a little will solve the second one too.
Please have a look at this excellent example, in which author talks about the problem you are facing.
http://www.ryanmwright.com/2010/04/25/dynamic-controlleraction-authorization-in-asp-net-mvc/

View elmah logs without creating role/user authentication

I started using ELMAH and I think its great. I read some documentation online but right now my app doesn't have roles/user authorization setup.
I would like to be able to read the error logs on production by accessing /elmah.axd but I don't want to just open it up to everyone, does elmah have any functionality that would allow me to create a password (in web.config) and pass it via querystring? Mimicking a "secure" RSS feed. Or something similar ofcourse....
but right now my app doesn't have roles/user authorization setup
Actually it's not that hard to configure forms authentication:
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880">
<credentials passwordFormat="Clear">
<user name="admin" password="secret" />
</credentials>
</forms>
</authentication>
and then protect elmah.axd:
<location path="elmah.axd">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</location>
and finally you could have an AccountController:
public class AccountController : Controller
{
public ActionResult LogOn()
{
return View();
}
[HttpPost]
public ActionResult LogOn(string username, string password)
{
if (FormsAuthentication.Authenticate(username, password))
{
FormsAuthentication.SetAuthCookie(username, false);
return Redirect("~/elmah.axd");
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
return View();
}
}
and a LogOn.cshtml view:
#Html.ValidationSummary(false)
#using (Html.BeginForm())
{
<div>
#Html.Label("username")
#Html.TextBox("username")
</div>
<div>
#Html.Label("password")
#Html.Password("password")
</div>
<p>
<input type="submit" value="Log On" />
</p>
}
Now when an anonymous user tries to access /elmah.axd he will be presented with the logon screen where he needs to authenticate in order to access it. The username/password combination is in web.config. I have used passwordFormat="Clear" in this example but you could also SHA1 or MD5 the password.
Of course if you don't want to configure authentication you could also configure elmah to store logging information in a XML file or SQL database and then completely disable the elmah.axd handler from your publicly facing website. Then create another ASP.NET site which only you would have access to with the same elmah configuration pointing to the same log source and simply navigate to the /elmah.axd handler of your private site to read the logs of your public site.

Resources