Server-Side Validation in MVC 3

This post is part of the series I’m doing on the newly released ASP.NET MVC 3.

In the first version of MVC there was very little support for validation.  Most developers simply used jQuery validation on the client and implemented their own validation on the server.  The release of MVC 2 came with several model validation improvements which have been enhanced even further with MVC 3.

How is it used

Let’s take a look at an example where validation helpers can be quite useful – user registration.

Validation for User Registration

Here’s a snippet from the View.

@Html.ValidationSummary(true, "Please correct the errors and try again.")
    
<div class="editor-label">
    @Html.LabelFor(m => m.Username)
</div>
<div class="editor-field">    
    @Html.TextBoxFor(m => m.Username)
    @Html.ValidationMessageFor(m => m.Username)
</div>

Pretty straightforward.  All the validation is made possible with attributes on the view model.

public class RegisterViewModel
{
    [Required(ErrorMessage = "Please enter a Username")]
    public string Username { get; set; }

    [Required(ErrorMessage = "Please enter a Password")]
    public string Password { get; set; }

    [Required(ErrorMessage = "Please confirm the Password")]
    public string ConfirmPassword { get; set; }

    [Required(ErrorMessage = "Please enter a valid Email")]
    public string Email { get; set; }
}

All of this is already possible with MVC 2.  Let’s add validation to say that Password and ConfirmationPassword must match.  There is no built-in attribute for this, so we need to write our own.  MVC 2 already had support for writing your own validation attributes, but this has been enhanced with MVC 3.

public class PasswordConfirmationAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var registerViewModel = (RegisterViewModel)validationContext.ObjectInstance;
        if (registerViewModel.Password != registerViewModel.ConfirmPassword)
        {
            return new ValidationResult(ErrorMessage);
        }

        return base.IsValid(value, validationContext);
    }
}

The ValidationContext is new in MVC 3 – this gives us context within which we can perform validation.  In MVC 2 we can only apply validation on a single field – we don’t know what the other values have been set to.

Custom Validation in Action

How to do this in MVC 2

This behaviour is completely possible in MVC 2, it’s simply a little more complex.  Since the we don’t have the ValidationContext object, we need to do our custom validation in the controller.

public ActionResult Register(RegisterViewModel viewModel)
{
    if (viewModel.Password != viewModel.ConfirmPassword)
    {
        ModelState.AddModelError(string.Empty, "The password and confirmation password must match");
    }

    if (ModelState.IsValid)
    {
        throw new NotImplementedException();
    }
    else
    {
        return View(viewModel);    
    }
}

This yields exactly the same behaviour.  So what’s the big deal – we’re still using exactly the same amount of code – it’s just in a different place?

The difference is that we need to remember to explicitly call this validation code every time we want to validate this model.  With validation attributes we can keep all the code in one place which is much neater.  The modifications to the validation helpers give us extra flexibility in dealing with different validation scenarios.

Further Reading

Scott Guthrie did a brief explanation on the validation enhancements when he announced the first preview of MVC 3.

Happy coding.

Tags: MVC

  1. Shane Whittet says:

    Great example. Your readers will also want to checkout MVC3's CompareAttribute here: http://www.asp.net/learn/whitepapers/mvc3-release-notes#_Toc276711789

  2. wehnertb says:

    One solution I used in MVC2 was to create a data annotation attribute. You can query the Request Object to get the value of the item to compare against and you can then annotate your ConfirmPassword field in your model.

    [Required]
    public string Password { get; set; }

    [Required]
    [MatchFields("Password")]
    public string ConfirmPassword { get;set;}

    The attribute looks like this:

    public sealed class MatchFields: ValidationAttribute
    {
    public string FirstField { get; set; }
    public override bool IsValid(object value)
    {
    string firstFieldValue = HttpContext.Current.Request[FirstField];
    if (value == null) return true;
    return value == firstFieldValue;
    }
    }

  3. Jaco Pretorius says:

    Very nice. I suppose it's not ideal to use the HttpContext directly, but it's definitely better than duplicating the logic in the controller.

  4. Srikanth says:

    Its look good, rather than HttpContext better to use server side Validation.