Client & Server Side Validation in ASP.NET MVC

[Update: The code is open-sourced at http://code.google.com/p/mvcvalidation/]

The asp.net mvc codeplex preview 5 version just came out a few days ago and it includes a lot nice enhancements.  For the best introduction read Scott’s post

Some of my favorite changes happened in validation.  Incidentally, I was already working on a post to talk about doing client and server side validation the easy way.  The inspiration came after reading Steve Sanderson’s post and watching his screencast on how he created a model-based client-side validation – very cool.

I want a simple way to perform client and server side validation for the application I am creating for my book and my objectives are simple (yet there is no easy way to do it):

  1. Have a model-based validation mechanism
  2. Have the least amount of repetition (DRY)
  3. Client side validation using jQuery
  4. Server side validation

First, I will show you how it works then I will explain how I got there.  Here is my model class for a user

   1: public class User
   2: {
   3:     public virtual string UserName { get; set; }
   4:     public virtual string Email { get; set; }
   5:     public virtual string Password { get; set; }
   6:     public virtual Guid ProviderId { get; set; }
   7: }

To add validation to it, I simply decorate it with the appropriate validation attributes.  It ends up looking like this:

public class User
{
    [ValidateNonEmpty("Username is required")]
    public virtual string UserName { get; set; }

    [ValidateNonEmpty("Email is required")]
    [ValidateEmail("Invalid email address")]
    public virtual string Email { get; set; }

    [ValidateNonEmpty("Password is required")]
    [ValidateLength(4,50,"Password should be at least 4 characters long")]
    public virtual string Password { get; set; }

    public virtual Guid ProviderId { get; set; }
}

I then generate the client script by adding 1 line of code to my view

<%=Html.ClientSideValidation("formCreateList", ViewData.Model) %>

On the server side, I simply make a call to a validation method from my controller

[Authorize]
[AcceptVerbs("POST"), ActionName("Create")]
public ActionResult Save(User newUser)
{
    AppHelper.ValidateModel(ViewData.ModelState, newUser);
    //the rest of the action code
}

If there are any validation errors on the server, I display the errors by using the framework helper method ValidationSummary.  All I have to do is add this 1 line to the view

<%=Html.ValidationSummary() %>

JavaScript

That’s all there is to it.  Now let’s talk about how it works.  The first challenge was generating the JavaScript to validate on the client. 

I decided to use the excellent jQuery Validation plugin.  The plugin gives me 2 ways to validate my form.  The first method uses classes on the the fields to be validated, for example a required email field would look like this

<input id="email" name="email" size="25"  class="required email" />

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

The second method uses rules that are defined in JavaScript and look like this

<script type="text/javascript"> 
$("#signupForm").validate({
        rules: {
            email: {
                required: true,
                email: true
            }
        },
        messages: {
            email: "Please enter a valid email address"
        }
    });
</script>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Check out the plugin documenation and demos to understand how it all works.

Since I was generating the script on the server and I wanted to make only one call, I went with the second option.

The script generator simply loops through all the model properties and their attributes and generate the appropriate script.  Afterthought: Now that I think about it, I could have easily generated a script to add classes to the elements using jQuery’s addClass method…  Oh well, maybe someone else will be kind enough to do it.

Castle Validator

I used the Castle Validator Component as my validation framework.  That’s where all the validation attributes you see above come from, but you can also create your own validators.  I then created a class called JQueryValidationProvider that implements IBrowserValidationGenerator interface in the Castle.Components.Validator namespace.  I haven’t implemented the entire interface and only have a couple of validations working (I will update the source code when it is all done).  Here is a couple of methods that I implemented.

public void SetEmail(string target, string violationMessage)
{
     Rules.Add(new ValidationRule(Validate.Email, violationMessage));
}
public void SetAsRequired(string target, string violationMessage)
{
    Rules.Add(new ValidationRule(Validate.Required, violationMessage));
}

As you can see, all I am really doing is just adding the validation rule to List of ValidationRules.  ValidationRule is a straight forward class and looks like this:

internal class ValidationRule
{
    public ValidationRule(Validate validate) : this(validate, null) { }
    public ValidationRule(Validate validate, string failureMessage)
        : this(validate, failureMessage, null) { }
    public ValidationRule(Validate validate, string failureMessage, object value)
    {
        Validate = validate;
        Value = value;
        if (failureMessage != null)
        {
            ErrorMessage = failureMessage;
        }
        else
        {
            //setup default error messages
            switch (Validate)
            {
                case Validate.Required:
                    ErrorMessage = "This field is required";
                    break;
                default:
                    ErrorMessage = "Field error";
                    break;
            }
        }

    }
    public readonly object Value;
    public readonly Validate Validate;
    public readonly string ErrorMessage;
}

The helper ClientSideValidation method simply loops through the collection of rules and generate the appropriate JavaScript.  Here it is

public static string ClientSideValidation(this HtmlHelper htmlHelper,
    string formName,
    object modelToValidate)
{
    var results = new StringBuilder();
    var rules = new StringBuilder();
    var messages = new StringBuilder();
    results.AppendFormat("$("#{0}").validate({1}", formName, "{");
    results.AppendLine();
    rules.AppendFormat("rules: {0}", "{");
    messages.AppendFormat("messages: {0}", "{");

    var props = TypeDescriptor.GetProperties(modelToValidate.GetType());

    var propCounter = 0;
    foreach (PropertyDescriptor prop in props)
    {
        var generator = new JQueryValidationProvider();
        foreach (var attrib in prop.Attributes.OfType<AbstractValidationAttribute>())
        {
            var v = attrib.Build();
            v.ErrorMessage = v.ErrorMessage ?? "*";
            v.Initialize(new CachedValidationRegistry(), null);
            v.ApplyBrowserValidation(null, InputElementType.Undefined,
                                     generator, null, null);
        }

        if (generator.Rules.Count > 0)
        {
            if (propCounter > 0)
            {
                rules.Append(",");
                messages.Append(",");
            }
            rules.AppendLine();
            messages.AppendLine();
            rules.AppendFormat("{0}: {1}", prop.Name.ToLower(), "{");
            messages.AppendFormat("{0}: {1}", prop.Name.ToLower(), "{");
            rules.AppendLine();
            messages.AppendLine();

            for (var i = 0; i < generator.Rules.Count; i++)
            {
                var rule = generator.Rules[i];
                rules.AppendFormat("{0}", rule.GetRuleString());
                messages.AppendFormat("{0}", rule.GetMessageString());

                if (i < generator.Rules.Count - 1)
                {
                    rules.Append(",");
                    messages.Append(",");
                }
                else
                {
                    rules.Append("}");
                    messages.Append("}");
                }
                rules.AppendLine();
                messages.AppendLine();
            }

            propCounter++;
        }
    }

    rules.Append("},");
    messages.Append("}");
    rules.AppendLine();
    messages.AppendLine();
    results.Append(rules.ToString());
    results.Append(messages.ToString());
    results.Append("});");

    return string.Format("<script type='text/javascript'><!--{1}{0}{1}--></script>", 
                            results.ToString(), Environment.NewLine);
}

Changes

Halfway through this post, I read Steve Sanderson’s new post and Stephen Walther’s new post on validation, I made some changes to the code.  I agree with their point that the controller shouldn’t be responsible for initiating the validation and it should be done at a lower layer.  I refactored the code and my controller now looks like this

[AcceptVerbs("POST")]
public ActionResult Create(User user)
{
    try
    {
        user = new User();
        UpdateModel(user, new[] { "name", "email" });
        var service = new UserService();
        service.Save(user);
    }
    catch (RuleViolationException ex)
    {
        MvcValidator.PopulateModelStateWithErrors(user, ViewData.ModelState, ex);
    }
    return View(user);
}

 

The validation gets initiated at the service (business logic) layer and if there is a rule violation an exception is thrown.  I then take the exception and pass it to a helper method that populates the model state. Note: I could probably rewrite this so that the ModelState population happens before the exception throwing which will eliminate the call to the population method.

My view looks like this

<%=Html.ValidationSummary() %>
<form id="formCreate" name="formCreate" 
        action='<%=Url.Action("Create")%>' method="post">
    <label for="name">Name</label>
    <input type="text" id="name" name="name" />
    <br />
    <label for="email">Email</label>
    <input type="text" id="email" name="email" />
    <br />
    <input type="submit" value="Submit" />
</form>
<%= Html.ClientSideValidation("formCreate", ViewData.Model) %>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

Now if I try to submit the form, the client script validation runs and I get these errors

clip_image001

If you get rid of client validation and only use server validation

to submit the form, the form would look like this

clip_image001[4]

Finally, here is the client JavaScript generated in the view:

<script type='text/javascript'><!--
    $("#formCreate").validate({
        rules: {
            name: {
                required: true,
                minlength: "3",
                maxlength: "25"
            },
            email: {
                email: true,
                required: true
            }
        },
        messages: {
            name: {
                required: "Username is required",
                minlength: "Username should be between 3 and 25 characters long",
                maxlength: "Username should be between 3 and 25 characters long"
            },
            email: {
                email: "Invalid email address",
                required: "Email is required"
            }
        }
    });
--></script> 

 

Pros & Cons

Here is what I think the pros of this design are:

  1. I love using model attributes to define validation rules
  2. I like that I only need 2 lines in my view
  3. I like that I don’t have to do anything in the controller other than catch an exception and call a helper method

The one thing I don’t like about this design is that it feels like there is a lot of dependencies.  I might need to refactor the code and move things around.  Here is my dependency diagram

image

Credit

This code was possible because of other people’s work:

Stephen Walther – link

Steve Sanderson – link and link 2

Hamilton Verissimo – link

Rick Strahl – link

Scott Guthrie – link

Source Code

Click here to download the rough-quality source code.

Let me hear what you think of this design.  What’s wrong with it?  What’s right?  Should I put this up on codeplex?

Advertisements

0 thoughts on “Client & Server Side Validation in ASP.NET MVC

  1. Emad – I think this stuff is looking promising. You have made the correct choice of inserting the JavaScript rules (as opposed to using classes to decorate the fields for two reasons I think:-CSS classes should be used for styling and grouping similar elements e.g. you could be using the class attribute already to style certain form elements or maybe using it to group specific elements which can then be manipulated with CSS or JavaScript jQuery etc. If you had used the class method for tagging the elements, you would have limited peoples use of them for other important reasons – so good choice :)We've rolled out a similar system (also based on Steve Sanderson's excellent work) and it's working well and is proving to be robust and extensible.Keep up the great work and looking forward to reading your book :)Mike

    Like

  2. I like this solution but it can't really validate some things, i.e. unique username..etc. It would be interesting to create an auto-ajax validator to go with asp.net mvc. In theory we could create a custom attribute that mapped a property to a an implementation of an interface and calls a HttpModule that knows how to look up that properties implementation of an interface and validate to see if the property is correct. Anyway…it might be way overkill, but I think this could be very useful in the long run.

    Like

    • Actually, it is pretty easy to do with jQuery's Validation plugin. One ofthe validators allows you to make a remote call… so you can easily callan action that checks if a username is unique and return a json result forthe jquery script to parse… I haven't done it but check the plugindocumentation for examples.

      Like

  3. Pingback: Reflective Perspective - Chris Alcock » The Morning Brew #177

  4. Pingback: To (ASP.NET)MVC or not to MVC (or, ASP.NET MVC Hyperlink Acupuncture) | The Freak Parade

  5. Pingback: ASP.NET MVC for Live Applications - Shiju Varghese's Blog

  6. Nice work, I like the optimisation of the writting of the client side validation.. but i do not like to be able to create an instance of the entities before validate with Castle.. I am trying to change this by a FormCollection…

    Like

  7. It did not compile for me with MVC Beta1 – I posted an issue on this to the project. btw I'm looking at modifying it to use System.ComponentModel.DataAnnotations instead of castle as then I can put the validation attributes in a metadata class instead of the class to be validated. My classes are generated so putting attributes on the fields is not an option.

    Like

  8. First off this is a great idea but It did not compile for me with MVC Beta1 – I posted an issue on this to the project.
    btw I’m looking at modifying it to use System.ComponentModel.DataAnnotations instead of castle as then I can put the validation attributes in a metadata class instead of the class to be validated. My classes are generated so putting attributes on the fields is not an option.

    Like

  9. Pingback: xVal - a validation framework for ASP.NET MVC « Steve Sanderson’s blog

  10. Hey Emad,Great work. I have a similiar issue to Jeremy's, but I am also doing what you are doing and abstracting the L2S data layer out and upkeeping my own model's. However I cannot set an attribute on the model to declare the validation rules as my model classes are being generated from CodeSmith…. yikes. What can I do so I can set this rules? Any ideas?

    Like

  11. Hi All,Emad, I hope you don't mind but I have proposed a change to your code at this point:signature declaration: public static string ClientSideValidation(this HtmlHelper htmlHelper, string formName, object modelToValidate, bool prefixWithModelName)and string builder:This caters for typical Model Binding scenarios where you include the model name as a prefix with a dot “.” and as a jquery validator plugin requirement, we need double quotes ” ” as seen here: http://docs.jquery.com/Plugins/Validation/Referrules.AppendLine(); messages.AppendLine(); string field = (prefixWithModelName ? """ + modelToValidate.GetType().Name + "." + prop.Name + """ : prop.Name); rules.AppendFormat("{0}: {1}", field, "{"); messages.AppendFormat("{0}: {1}", field, "{"); rules.AppendLine(); messages.AppendLine();I also made a change to leave fields as is without ToLower() to comply with my forms and class naming standard.I suppose to remove complexity entirely, all names could just be wrapped in double quotes though. Shrug.This is working well for me.

    Like

  12. Pingback: ASP.NET MVC Validation - The Definitive Guide (in my eyes) « {Programming} & Life

  13. Pingback: Buu Nguyen’s Blog » Blog Archive » ASP.NET MVC Validation Library

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s