14 Days Free Technical Video Training from WintellectNOW

  

How data annotations for ASP.NET MVC validation work

Tags: ASP.NET, ASP.NET MVC, ASP.NET Web Forms

Data validation is one of the most important aspects of developing applications for the web. However, validation is also something that can get messy pretty quickly, as developers often stick validation code anywhere and everywhere. However, if you keep a clear separation of concerns by using the MVC pattern and couple that with data annotations in the model, then your validation code becomes much simpler to write, maintain, and test. The added bonus: simple code that's well written & tested is code that's just going to work.

What are data annotations?

Data annotations are attribute classes that live in the System.ComponentModel.DataAnnotations namespace that you can use to apply to (decorate) classes or properties to enforce pre-defined validation rules. These annotations are available across various Visual Studio 2010 project types including ASP.NET MVC, Web Forms, ASP.NET Apps & Web Sites, Dynamic Data & even non ASP.NET projects like Silverlight & WPF. The same holds true for the data models; you can use these annotations with POCOs (plain old CLR objects), EF models, Linq2SQL models, etc... The bottom line is that you can use data annotations anywhere.

There are several out of the box data annotations to choose from:

  • Required
  • Regular Expression
  • Range
  • ZipCode
  • DisplayName
  • DisplayFormat
  • Scaffold
  • DataType
  • StringLength
  • A few more...

Although having this set of built in validations is great, it is a limited set. Since every business has lots of rules that don't neatly fit into this pre-defined set of attributes, what do you do when you need to use your own logic? It's quite easy, really - just derive from any of the inheritable (non sealed) attribute classes in the System.ComponentModel.DataAnnotations namespace, then code your business rules in the derived class methods.

Between the built-in validators and your own custom validation code, you're all set with maintainable and clean validation. However, server side data sanitization & validation is also equally important. An upcoming blog post will cover creating custom & server side validation. In the meantime, Brad Wilson from the ASP.NET team has some great blog posts on unobtrusive validation along with a a great series of posts on MVC.

And it all starts in the data model...

Applying data annotations to the model.

Suppose a data entry clerk needs to frequently update baked products with baked and expiration dates, amounts, prices, etc... To accomplish this in code, start with a basic POCO class to represent a product and will serve as the data model to meet the needs of this scenario. You'll need to do more than just create the class and its members though, and that's where annotations come into play.

Data annotations serve as a way to enforce these common validation scenarios without having to write much code, and more importantly, without writing repetitive code. Annotations live in one place - the model, rather than a code behind, web page, controller, or view. This helps prevent data validation code from turning into Spaghetti# code[1] scattered about your project.

Take a look at the following Product class, complete with a few basic data annotations:

public class Product 
{
public int Id { get; set; }

[DisplayName("Delicious Treat")]
[Required(ErrorMessage = "The product name field is required.")]
public string Name { get; set; }

[Required(ErrorMessage = "The product description field is required.")]
public string Description { get; set; }

[DisplayName("Sale Price")]
[Required(ErrorMessage = "The Sale Price field is required.")]
public decimal Price { get; set; }

[DisplayName("Freshly Baked on")]
[Required(ErrorMessage = "The Freshly Baked On field is required.")]
public DateTime CreationDate { get; set; }

[DisplayName("Don't Sell After")]
[Required(ErrorMessage = "The Expiration Date field is required.")]
public DateTime ExpirationDate { get; set; }

[DisplayName("Qty Available")]
[Required(ErrorMessage = "The Qty Available field is required.")]
[Range(0,120)]
public int QtyOnHand { get; set; }

[DisplayName("Product Image")]
public string ImageName { get; set; }
}

The above properties are labeled with attributes that clearly state their intentions. The bulk of the validation is simply the Required attribute for this sample, along with a Range attribute on the QtyOnHand property. Some attributes such as the DisplayName attribute don't necessarily validate but do affect the rendered output. As the above sample demonstrates, any combination of necessary attributes can decorate properties. Notice that all the model and validation code so far live in the same place, and will continue to do so when you create custom attributes and validators; meaning that you'll continue to have a nice, clean separation of concerns in your app.

Once you're done applying attributes to the model, you can move on to the controllers and views.

Where do views & controllers fit in?

Views:

Once you've setup validation on the model using data annotations, they're automatically consumed by Html Helpers in views so the helpers can render the proper HTML output. For client side validation to work, you will need to ensure that these two <SCRIPT> tag references are in your view:

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<
script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script
>

Note: If you're using ASP.NET MVC 3 project templates, these script tags will be added in views automatically for you by selecting the "reference client script libraries" option in the Add View dialog.

The following code from an edit or insert view will make sure the DisplayName, Required, and Range attributes work at run time.

<div class="editor-field">
@Html.EditorFor(model => model.QtyOnHand)
@Html.ValidationMessageFor(model => model.QtyOnHand)
</div
>

Other helper methods such as @Html.TextBox, @Html.TextBoxFor, @Html.Editor, etc... render nearly identical yet very slim output, explained more later in this post. Views play an integral part in validation, yet as the developer you often won't need to do much in the view other than use the built-in Html Helpers.

Controllers:

Controllers serve as a gateway to accept HTTP POST requests to process incoming data then to match that data with its model and view for rendering. Controllers shouldn't be a dumping ground for validation code, server side validation code should be called from controller action methods. While you can put validation code directly into the action methods themselves; the practice tends to lead to code that's less than optimal to maintain, since you've started scattering the code about the project. Rather than having code in multiple locations, think about moving server side validation code to a single location near or in the model, or as its own library project.

How validation works.

Both client and server side validation work because of a few conventions in your project that match up data annotations, Html Helpers, rendered output. Html Helpers in views render HTML elements containing attributes that start with the pattern data-val-*. The data-val-* attributes contain error messages, regular expressions, ranges, and other validation information that originates in data annotations.

That means that this decorated code in the model...

[DisplayName("Qty Available")]
[Required(ErrorMessage = "The Qty Available field is required.")]
[Range(0,120)]
public int QtyOnHand { get; set; }

...combined with this code in the view...

<div class="editor-field">
@Html.EditorFor(model => model.QtyOnHand)
@Html.ValidationMessageFor(model => model.QtyOnHand)
</div
>

...turns into this HTML at runtime...

<div class="editor-field">
<input class="text-box single-line"
data-val="true" data-val-number="The field Qty Available must be a number."
data-val-range="The field Qty Available must be between 0 and 120."
data-val-range-max="120" data-val-range-min="0"
data-val-required="The Qty Available field is required."
id="QtyOnHand" name="QtyOnHand" type="text" value="12" />
<span class="field-validation-valid" data-valmsg-for="QtyOnHand" data-valmsg-replace="true"></span
>
</
div
>

The tie-in between the data model annotations and the data-val-* attributes should be clear after reading the above code, but it's where the client side validation ties in might not be so obvious. Open the \Scripts\jquery.validate.unobtrusive.js file and search for "data-val". Right away you'll see that the JavaScript uses the data-val-*, input-* and field-* CSS classes to display/hide validation messages on the client. Although you shouldn't modify or need to maintain the built-in .js files; it's worth investigating them to see how things work together in ASP.NET MVC.

function onError(error, inputElement) {  // 'this' is the form element
var container = $(this).find("[data-valmsg-for='" + inputElement[0].name + "']"),
replace = $.parseJSON(container.attr("data-valmsg-replace")) !== false;

container.removeClass("field-validation-valid").addClass("field-validation-error");
error.data("unobtrusiveContainer", container);

if (replace) {
container.empty();
error.removeClass("input-validation-error").appendTo(container);
}
else {
error.hide();
}
}

Having the onError function tied in by only HTML attributes keeps client side validation unobtrusive, or in other words, out of the way of your view code. Annotations combined with unobtrusive validation make both the view and the output are very clean and maintainable. The final result in the browser is fully capable client side validation that falls back to server side validation for browsers without JavaScript enabled. Either way, the same validation happens and the user sees the same error.

image_thumb2

Summary & Code

Data annotations give you a nice way to keep validation and business logic close to the model, and easier to maintain, test and debug. Additionally, cleaner, simpler output leads to faster download speeds and easier integration with client side script libraries like jQuery for UI manipulation.

Although this post focuses on data annotations and client side script, don't forget that server side validation is very important.

Download the code.

Notes: [1] Spaghetti# is not a real language.

22 Comments

  • Rachel said

    Rajesh,

    Put your validation in a partial class. Viewmodels are generally used when you need to combine different models together.

  • Amit Mangal said

    Great. Worked for me on a simple mvc project.
    But need to implement it in a real client project now and that has no aspx page (views are cs pages generating html content using Nodes). Do these 'Html.ValidationMessageFor(model => model.QtyOnHand)' work in code behind as well ? will have to check out.
    Also, these had an inbuilt support for creating automatic client side validation by simply using 'Html.EnableClientValidation()' with a couple of js files (MicrosoftAjax.js, MicrosoftMvcValidation.js). Will have to check this out too.
    And ya, all this in mvc1 :-/. best of luck to me

  • Rachel said

    @Samuel,

    Yes, since the attributes I've mentioned are from the System.ComponentModel.DataAnnotations namespace, they're available to EF, Web Forms, MVC, SL/WPF, etc... Pretty much all technologies.

  • Dom Brown said

    I wonder if you can help, i'm trying to use these for data validation on a textbox where the user enters their favourite colour.

    I want to be able to make it so they can only enter colours i specify, "red", "Blue", "Green" etc

    I have managed to get it working for 1 colour using [RegularExpression("Red")] but i cannot enter more as it comes up with errors.

    I am a total noob so forgive me if this is something really simple, any advice would be great!!

  • Rachel said

    Dom,

    Consider using a dropdownlist, as that will limit the choices, which is what you want. You can also validate with a [Required()], then the user has the freedom to choose one, but still must actually choose.

    I have a post in the works about doing this that I plan on publishing next week-ish (soon).

    In the meantime, this search link has a lot of good results for you to look over - it should get you started in the right direction.

    http://www.bing.com/search?q=asp.net+mvc+dropdownlist&go=&qs=n&sk=&sc=5-18&form=QBLH

  • Dom Brown said

    Hi,

    afraiud i wasn't allowed to use a drop down, my boss was trying to teach me something lol

    i've managed to make a customvalidation with a valid if statement for the basic colours.

    i wasn't aware you had to make another class for a customvalidation, guess that was my lesson.

    I'll keep an eye open for your post as i am sure its a good read

    Thanks

  • JR said

    hi. just want to check if it is possible to set data annotation at runtime. my model is retrieved from db at runtime and I would like to set the UIHint, and other data annotations that I normally set at the model. if it is not possible, what would be the best workaround. thanks in advance. love your posts...

  • Rachel said

    JR

    Take a look at a custom ModelValidatorProvider. Brad Wilson wrote about creating one w/Entlib here:

    http://bradwilson.typepad.com/blog/2009/10/enterprise-library-validation-example-for-aspnet-mvc-2.html

    This on StackOverflow is a smaller sample
    http://stackoverflow.com/questions/3607247/dataannotations-dynamically-attaching-attributes

    Another alternative is using reflection, but there could be performance issues, and the code wouldn't be as nice.

    Hopefully those options will work nicely for you.

  • moncler said

    Data validation is one of the most important aspects of developing applications for the web. However, validation is also something that can get messy pretty quickly, as developers often stick validation code anywhere and everywhere. However, if you keep a clear separation of concerns by using the MVC pattern and couple that with data annotations in the model, then your validation code becomes much simpler to write, maintain, and test. The added bonus: simple code that's well written & tested is code that's just going to work.

  • Nagendra said

    In the bellow program the "Fname" feild its doesn't accept the Numbers(0-9),i am writing validation in the model class called "GuestResponse.cs"but its accepting the number

    GuestResponse.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.ComponentModel.DataAnnotations;

    namespace newValidation.Models
    {
    public class GuestResponse
    {
    [Required(ErrorMessage = "Enter the Proper EmpId")]
    [RegularExpression ("/[0-9]/",ErrorMessage ="enter the valid EmpId")]
    public int EmpId { get; set; }

    [Required (ErrorMessage="Enter the fname ")]
    [RegularExpression ("/[A-Za-z]/",
    ErrorMessage ="Please Enter the valid Email Id")]
    public String fname {get;set;}

    [Required(ErrorMessage = "Enter the Mname")]
    [RegularExpression ("/[A-Za-z]/",
    ErrorMessage ="Enter the Proper Mname")]
    public String mname { get; set; }

    [Required(ErrorMessage = "Enter the Lname")]
    [RegularExpression("/[A-Za-z]/", ErrorMessage = "Enter the Proper Lname")]
    public String lname { get; set; }

    [Required(ErrorMessage = "Enter the Address")]
    [RegularExpression("/[A-Za-z0-9]/", ErrorMessage = "Enter the Proper Address")]
    public String Addres { get; set; }

    [Required(ErrorMessage = "Enter the username")]
    [RegularExpression("/[A-Za-z0-9]/", ErrorMessage = "Enter the Proper UserName")]
    public String Username { get; set; }

    [Required(ErrorMessage = "Enter the Password")]
    public String Password { get; set; }

    }
    }


    Index.cshtml

    <html >
    <head>
    <title></title>
    <link rel="Stylesheet" href="@Href("~/Content/Site.css")" type="text/css"/>
    </head>
    <body>
    </body>
    </html>
    @using newValidation.Models;


    @using (Html.BeginForm("SaveChanges","Home", FormMethod.Post))
    {
    @Html.ValidationSummary()

    <label for="Name">Empid</label>

    <input class="input-validation-error" type="text" id="empid" name="empid"/> <br />

    <label for="fname">Fname</label>
    <input class="input-validation-error" type="text" id="Fname" name="Fname" /><br />

    <label for="mname">Mname</label>
    <input class="input-validation-error" type="text" id="mname"name="mname" /><br />

    <label for="lname">Lname</label>
    <input class="input-validation-error" type="text" id="lname" name="lname" /><br />

    <label for="address">Address</label>
    <input class="input-validation-error" type="text" id="address" name="address" /><br />

    <label for="Username">UserName</label>
    <input class="input-validation-error" type="text" id="UserName" name="UserName" /><br />


    <label for="Password">Password</label>
    <input class="input-validation-error" type="password" id="Password" name="Password"/><br />



    <input type="submit" value="Submit" onclick="show_alert();"/>


    }



    @using (Html.BeginForm("Show_Result", "Home", FormMethod.Post))
    {

    <input type="submit" value="Show Result" onclick="Show_Result()" />
    }


    <table id="nametable">
    <tr>
    <th>Empid</th>
    <th>Fname</th>
    <th>Mname</th>
    <th>Lname</th>
    <th>Address</th>
    <th>UserName</th>
    <th>Password</th>
    </tr>
    @{
    if (ViewData["Data"] != null)
    {
    foreach (newValidation.Models.Name n in (List<Name>)ViewData["Data"])
    {
    <tr>
    <td>@n.EmpId </td>
    <td>@n.Fname</td>
    <td>@n.Mname</td>
    <td>@n.Lname</td>
    <td>@n.Address</td>
    <td>@n.Username</td>
    <td>@n.Password</td>
    </tr>


    }
    }
    }


    </table>

    <script type="text/javascript">
    function show_alert() {
    var empid = document.getElementById("empid").value;
    var fname = document.getElementById("fname").value;
    var mname = document.getElementById("mname").vaue;
    var lname = document.getElementById("lname").value;
    var address = document.getElementById("address").value;
    var username = document.getElementById("username").value;
    var password = document.getElementById("password").value;

    alert("Empid:" + empid + "Full Name:" + fname + mname + lname + "Address:" + address);


    }
    </script>

  • Rachel said

    Nagendra,

    Take a look over at regexlib.com, as your RegEx isn't quite right. Try some different expressions for alphanumeric input on that site.

    Also, for first, last, address, etc...there's usually no need to limit to alpha characters only. Lots of names have apostrophes or other characters, so you could potentially remove those attributes (unless you have a compelling reason otherwise, which could be the case).

  • Raechel said

    And then there is the pesky... change your underlying data model...lose all your custom annotations. Any idea how to keep the annotations from being lost when even the most minor change is made to the database?

  • Spamme said

    Nice article, thanks.

    I would have liked to see the validation in the controller, but you didn't cover it. The project download link doesn't work either.

  • Rachel said

    Spamme,

    Thanks for the feedback, yes, that is a popular request, so I'll be investigating that type of post. Sorry about the link, I can't seem to find that sample. :(

    I'll post it when I do

  • hs said

    There is someone telling how to integrate it with knockout here: http://stackoverflow.com/questions/5741658/knockout-mvc-3-validation/6559734#6559734

  • jedh said

    @hs There is something new about here : http://sixgun.co.uk/blog/2012/03/28/asp-net-web-api-validation-with-data-annotations/

Comments have been disabled for this content.