Udostępnij za pośrednictwem


Granular Request Validation in ASP.NET MVC 3

12/10 Update: In MVC 3 RC 2 SkipRequestValidationAttribute got renamed to AllowHtmlAttribute. I have updated the examples below.

A little while ago I wrote a blog post describing granular request validation that shipped in MVC 3 Beta. However, since then we have changed the API for this feature and that post is no longer valid. In this post I will present the new API which is usable in the recently-shipped MVC 3 Release Candidate.

But first: a quick refresh on request validation and why it’s great to make it granular. Request validation is a feature of ASP.NET that analyzes the data that a browser sends to the server when a user interacts with your site (such as form or query string data) and rejects requests that contain suspicious input that looks like html code (basically anything with a ‘<’). This protects you from HTML injection attacks such as cross-site scripting (XSS). It is enabled by default, however in previous versions it was an all-on-or-off feature, meaning that if you want to be able to accept HTML-formatted input from your users in just one field you had to completely turn this protection off. This in turn meant that you now had to validate every bit of data that came from the client.

AllowHtmlAttribute SkipRequestValidationAttribute

In MVC 3 we are introducing a new attribute called AllowHtmlAttribute. You can use this attribute to annotate your model properties to indicate that values corresponding to them should not be validated. Let’s take this User model and UserController as an example:

 public class User {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    [AllowHtml]
    public string Description { get; set; }
    [AllowHtml]
    public string Bio { get; set; }
}

public class UserController {
    [HttpPost]
    public ActionResult Update(User user) {
        // update user database
    }
}

I have annotated the Description and Bio properties to indicate they should not be request-validated. Now when the Update action method gets invoked these two properties on the User object will not be validated and any HTML they might contain will be passed straight through to the action method. However, everything else will still go through request validation and requests that contain suspicious content in the Name or Email fields will get rejected.

ValidateInputAttribute

AllowHtmlAttribute can only be applied to properties of a model class. For other request validation scenarios the existing ValidateInputAttribute is still helpful. For example, you can use it to disable request validation for action methods that bind to a loose collection of parameters:

 [ValidateInput(false)]
public ActionResult Update(int userId, string description) {
}

Now when the parameters of the Update method get bound request validation will not be performed. You can apply ValidateInput to action methods as shown above or to the entire controller to affect all of its action methods.

ValidateInput is also more usable in MVC 3. In MVC 2 running on .NET 4 you had to set requestValidationMode="2.0" in order to turn request validation off. In MVC 3 this is no longer necessary.

Comments

  • Anonymous
    January 20, 2011
    I tried using [ValidateInput(false)] in MVC3 on a controller action method with no avail. I still get the: A potentially dangerous Request.Path value was detected from the client (<). We don’t want to allow scripting, but to the user that may have accidently typed a < or > or % the error is not acceptable. I don’t want to go back to 2.0 validation for the entire site. Is there a way to accomplish this elegantly?

  • Anonymous
    January 20, 2011
    I have also tried using the [AllowHtml] attribute with similar issues. The problem is we have many site specific routes and our own override of the IHttpModule which checks for specific Request attributes. When this happens, the underlying code validates the request and throws a similar validation error. Any hints would be great.

  • Anonymous
    January 20, 2011
    Gary, the granular request validation in MVC 3 only supports turning off validation for Form (and sometimes QueryString). Path will still be validated, as well as the remaining Request collections. Your module should access the Request values via the System.Web.Helpers.Validation.Unvalidated() method (available in System.Web.WebPages.dll) to get around the default request validation (that's what's being used by the mechanism that allows AllowHtml to work).

  • Anonymous
    January 20, 2011
    I will take a look at that. I'm also seeing the same problem with Routes we have built using MapRoute. It doesn't even give us a chance to get to the validation before the same error is thrown. Is there a way around that?

  • Anonymous
    January 20, 2011
    Gary, I can't really tell what problems you might run into without more information. I would suggest that you posted some sample repro code or stack trace information on stackoverflow.com or forums.asp.net so that you get access to more people who could help you. I frequent those sites too so will be able to respond to your questions there.

  • Anonymous
    January 20, 2011
    Thanks

  • Anonymous
    February 24, 2011
    I am new to ASP.NET and am finding the reasoning operation of request validation a little strange. The validation itself is great, but the fact that the user entering <> causes the app the just die isn't! The other MVC entity validation is great at automatically updating the modelstate and redisplaying the page. Why doesn't request validation work in the same user friendly way? I do not want to allow html input, but at the same time I do not want the app to crash if the user does inadvertently trigger it.