Simple and Custom Validation in MVC 3

Tags: ASP.NET, MVC, Validation

This is the first of a series on validation and business rules in MVC 3. I'll start out simple and we'll work out way up to the more complex. I'm going to build on the solution created in the previous blog post (Delete Like a Rock Star) so go there and get set up.

Ok. MVC 3 models (POCOs, classes, whatever) can take advantage of a whole host of validation attributes located in System.ComponentModel.DataAnnotations. Throw the object browser on there and you can see them all. Let's take a basic one. Who doesn't love a required field? Let's put one on our Widget name:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using YourSolutionNamespace.Validators;
 
namespace YourSolutionNamespace.Models
{
    public class Widget
    {
        public int Id { get; set; }
 
        [Required]
        public string Name { get; set; }
    }
}

Notice I'm using System.ComponentModel.DataAnnotations. Before we fire up the app comment out the two lines in the controller's Create method that would ordinarily call EF:

[HttpPost]
public ActionResult Create(Widget widget)
{
    if (ModelState.IsValid)
    {
        //db.Widgets.Add(widget);
        //db.SaveChanges();
        return RedirectToAction("Index");  
    }
 
    return View(widget);
}

Now fire up the application, navigate to Widgets, and click the Create new link to be taken to the create new widget view. Try to save it without entering in a value for the name field. You get some nice out-of-the-box behavior. Now let's write a custom validator. Let's say that a new widget name cannot be the same as one already in the system. We'll need a custom validator called NameAvailableAttribute that encapsulates this requirement. Create a new folder called Validators. Then drop this class into the new folder:

public class NameAvailableAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        if (value == null)
        {
            return true;
        }
 
        if ((value is string) && string.IsNullOrEmpty((string)value))
        {
            return true;
        }
 
        var name = value.ToString().ToLower();
 
        // fake a database lookup and bring back two widgets
        var widgetNames = new List<Widget>()
        {
            new Widget() { Id = 1, Name = "SquareWidget"},
            new Widget() { Id = 2, Name = "RoundWidget"}
        };
 
        return (!widgetNames.Any(w => w.Name.ToLower().Equals(name)));
    }
 
    public override string FormatErrorMessage(string name)
    {
        return ValidationMessagesResource.NameIsTaken;
    }
}

Two things here: I'm not talking to a database but just faking it out (in a future blog post I'll expand on this code). Second, I put an error code in a resource table and from FormatErrorMessage I just call it. You could just as easily hard-code the string here. Call me crazy but I like string tables for this sort of thing.

Now we need to decorate our Name property in the Widget class. Reference the namespace to the new validator and add the new attribute:

[Required]
[NameAvailable]
public string Name { get; set; }

Build and fire up the app and go through the process of creating a new widget again. This time enter something like "Purple Widget" and make sure it behaves correctly. Then enter "SquareWidget" and you should see your custom error message.

That's it for now. Places to go from here are client-side AJAX calls to our custom validators for database lookup scenarios, implementing class-level validation with IValidatableObject, and using Ninject to inject business rules (or services that know how to get them) into your model. These are topics for future blog posts.

Add a Comment