A A
RSS

Client & Server Side Validation in ASP.NET MVC

Mon, Sep 8, 2008

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" />

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>

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) %>

 

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?

Tags: , , , , , ,

  • fred
    There is another validation toolkit called Validator Toolkit for ASP.NET on Codeplex.com. See http://www.codeplex.com/MvcValidatorToolkit
  • 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/Reference#Fields_with_complex_names_.28brackets.2C_dots.29


    rules.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.
  • 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?
  • I am not sure what to do here. That is the problem with generated code and
    it is the same problem with using LINQ 2 SQL, you can't add attributes to
    the generated code.
    -Emad
  • Emad,

    I got past this problem by using System.ComponentModel.DataAnnotations, I am using a feature in Dynamic Data to achieve this (http://blogs.microsoft.co.il/blogs/noam/archive/2008/05/12/screencast-how-to-work-with-dataannotations-vs2008-sp1-beta.aspx) lucky find, but working well!

    I had to update my last posted example with a few bigger changes to support this. My last code still stands if you can add attributes to your model, as said I couldn't as mine was code generated. I have just outlined the approach I took in my blog (http://goneale.wordpress.com/2009/01/19/aspnet-mvc-validation-the-definitive-guide-in-my-eyes/) rather than spamming yours. :)
  • 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.
  • Jeremy Thomas
    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.
  • I have updated the source code to compile and work with MVC Beta 1. Get it from the google project page at http://code.google.com/p/mvcvalidation/
  • I think that code was written using MVC Preview 5. You will have to modify
    it to work with the Beta.
    I like using the DataAnnotations namespace instead and have switched to it
    in one of my projects.

    I use POCO classes for models but I write conversion functions that convert
    and from my POCO to my LINQ to SQL entities. Some grunt work but works
    well.
  • I couldn't receive code from Google SVN repository! Anything wrong with SVN URL on Google Code?
  • It's working fine for me. Did you see the instructions at
    http://code.google.com/p/mvcvalidation/source/checkout
  • jb
    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...
  • Nice work on this. I have just been digging into this plugin and the helper is a great idea.
  • justin
    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.
  • Actually, it is pretty easy to do with jQuery's Validation plugin. One of
    the validators allows you to make a remote call... so you can easily call
    an action that checks if a username is unique and return a json result for
    the jquery script to parse... I haven't done it but check the plugin
    documentation for examples.
  • Emad -- This is great! I really like how you integrated JQuery to do client-side validation.
  • That is very nice... I implemented some of the livevalidation from Steve Sanderson. I like what you have done w/ it using Preview 5. I look forward to digging through your post in more detail!
  • Domagoj Barisic
    Great code!

    Put this on CodePlex
  • I put it on google code because I wanted to try it out.. It's at http://code.google.com/p/mvcvalidation/
  • Nice and helpful post. But I've one question, if I want to use livevalidation or other validation javascript then what should I do?
  • Check out setve's post and code, he uses livevalidation
    http://blog.codeville.net/2008/04/30/model-based-client-side-validation-for-aspnet-mvc/
  • HI Emad ,

    Nice post and looking good.
    Which book your writing?
  • Thanks. The current title is "ASP.NET MVC Test Driven Development Problem Design Solution" and the publisher is Wrox.
  • 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
  • Thanks and glad to get some 3rd-party validation (no pun intended) for my design.
blog comments powered by Disqus
Advertise Here
The Most Intelligent Add-In To Visual Studio Happy fan of

What I'm Doing...

Yonkly Open Source

Sign up for my newsletter




* = required field

powered by MailChimp!

megree Widget

Apparently, I am connected to Obama. Check this out...
My path to Obama

Cyber Identity