Twitter About Home

Thoughts on validation in ASP.NET MVC applications

A couple of months back, I supplied some code for doing model-based validation in an ASP.NET MVC application in a way that automatically generates client-side validation JavaScript. That was pretty popular. Lots of people are enthusiastic about expressing validation rules as attributes on model properties. However, ASP.NET MVC has been enhanced since then, and so have my views on what constitutes tidy and effective validation.

Published Sep 8, 2008

So, let’s start with the new technology. MVC Preview 5 comes with two new API pieces that seem relevant to validation:

  • ModelState. Controllers now have an official way to pass validation error information to views. Populate ViewData.ModelState, then you can use the built-in HTML helpers to render error message summaries, and to highlight input fields that correspond to errors.
  • Model binders. Controllers are expected to populate model objects from form posts using the IModelBinder API. You can create custom binders – the framework calls your binder to obtain a value for each model property. (If you don’t understand this, don’t worry – it turns out not to be relevant.)

The obvious thing to do (and I’ve seen several forum posts and blog comments suggest this) is to make a custom model binder that applies validation rules (maybe ones expressed as attributes on model properties, such as [IsRequired]) and populates ModelState with any errors it finds. However, there are technical issues that make such a design unworkable in Preview 5 (model binders don’t get told what type of model object they’re providing values for). And more importantly than the current technical issues, that sort of design would be totally undesirable anyway. Validation has nothing to do with controllers binding form posts to model objects, as I’ll explain.

In the last week, Scott Guthrie posted about handling form posting/validation scenarios in ASP.NET MVC, and so did Stephen Walther. The following design has some things in common with each of their designs, but some important differences too.

Basic principles

So, how do you design a pattern for validation? Like any design, start by deciding what characteristics are really important to you, and build from there. Here’s what I consider to be important truths of validation:

  1. Model objects should perform *and *enforce validation at the appropriate time.
  2. Validation rules should be expressible in plain old C#.
  3. Validation rules and business rules are at opposite ends of the *same *spectrum.
  4. Errors should bubble up to the UI automatically (no code needed).
  5. Client-side validation is optional – it’s purely for convenience.

And what does that mean?

1 says that model objects don’t merely hold validation rules (e.g., as attributes on their properties), but model objects also actually enforce those rules. So, *it’s not acceptable for a controller class to make the decision to validate **(as they do in many other examples) – that would undermine the model’s encapsulation, and would mean that a badly-behaved controller could choose to skip validation. It’s the *model that enforces validation, and it does so at a time of its own choosing – usually when something is being committed (e.g., when the model object is being saved to the database), but it might also choose to validate simple property formatting rules during property setters.

Now, if the model is capable of enforcing validation at a time of its own choosing, then it needs a general-purpose mechanism to forcibly abort certain controller operations when validation rules are violated. Fortunately, C# comes with the perfect mechanism: exceptions. There’s nothing clever about this (simplicity is the goal) – when the model decides it won’t allow an operation, it throws an exception. The operation is forcibly aborted. The controller must obey.

Also, bear in mind that certain validation rules can only be enforced at the point of committing data or finalizing actions. For example, “usernames must be unique” will probably be enforced in your database (with a UNIQUE constraint). You therefore can’t rely on a single “validation” moment – the model has to be free to issue rule exceptions at any point during its processing, and the controller shouldn’t need any special-case logic to deal with this. This requirement fits quite easily into the following design.

2 just says that I don’t want to rely on any kind of rules engine. Now I’m perfectly happy to have reusable attributes to represent certain simple property formatting rules (e.g., [IsRequired], [DataFormat(Formats.EmailAddress)]), and you could even build a custom rules engine, but these are built on the fact that arbitrary C# is allowed.

[3] tries to clarify that simple property formatting rules (e.g., must be valid SSN) are a good start, but not adequate on their own. Certain business rules describe whether or not an operation is allowed (e.g., usernames must be unique), might relate to interactions of multiple properties, might involve arbitrarily complex logic, and these ought to fit into the validation framework too. See the diagram below.

image_thumb2

[4] is about making the system easy to maintain. When I add a new validation rule to my model, I want all controllers/views that work on that model type to render suitable error messages automatically, without changing any controller/view code.

Now, remember that when the model decides some rule has been violated, it throws an exception. If that exception is of some special type (let’s call it BusinessRuleException), then that exception can describe in some strongly-typed way which rule was validated and how. In fact, it can describe multiple rule validations simultaneously. All we need now is for controllers to catch BusinessRuleExceptions, use them to populate ViewData.ModelState, then ASP.NET MVC’s built-in helpers will take care of displaying the messages. We can do localization in this step too, for multilingual error messages.

[5] says that it’s nice to have a helper to generate client-side validation code automatically, but the helper doesn’t have to replicate every rule on the client. Obviously, since we’re allowing arbitrary C# rules, there’s no automatic translation of arbitrary rules to JavaScript. Sometimes client-side validation is impossible anyway (e.g., “usernames must be unique”). However, if some simple property formatting rules are implemented as attributes (e.g., [IsRequired]), then we can generate client-side code from these attributes. It won’t cover all rules, but it will cover many of them, and that’s good enough.

Time for some code plz

Yes I know – reading English *is *hard work. Far easier if I show you some C# code. Here’s how a model class might look:

public class Person
{
    [Required] [StringLength(20)]
    public string Name { get; set; }
 
    [Required] [Range(1, 200)]
    public int? Age { get; set; }
 
    public void EnsureValid()
    {
        // If any of this object's property values conflicts with a validation [Attribute],
        // it will add an entry to the violations collection
        RuleViolations violations = ValidationHelpers.RunValidationAttributes(this);
 
        // Now we can run any other custom C# validation logic
        if(Age.HasValue && ((DateTime.Now.Year - Age.Value) % 4 == ))
            violations.Add(new ArbitraryViolation("Age", "Sorry, you were probably born on a leap year, so we won't let you register."));
 
        if (!violations.IsEmpty)
            throw new BusinessRuleException(violations);
    }
}
 
public static class PersonRepository
{
    public static void SavePerson(Person person)
    {
        // The model is reponsible for deciding when to validation.
        // It chooses to validate now. If there's a violation, it will
        // throw an exception
        person.EnsureValid();
 
        // Good - it was valid. Todo: now save to database
    }
}

As you can see, there are some simple property format rules implemented using attributes (these are the standard ones shipped in System.ComponentModel.DataAnnotations), but these are just a special case. You can of course use arbitrary C# logic when enforcing validation.

I don’t need to have a special method marked as the validation method (or an IValidatable interface or anything like that), because the model layer makes its own decision when to validate, and chooses how to do that. It just has to throw a BusinessRuleException if there’s a problem. In this example, it chooses to enforce validation before writing to the database, but you could choose to validate certain properties during their setter methods if you wanted.

Next, how does a controller look? It follows a general pattern like the following. The critical bit is that model operations are wrapped in a try…catch so that it can recover from errors and display suitable UI feedback.

public class RegistrationController : Controller
{
    [AcceptVerbs("GET")]
    public ViewResult Edit() { return View(new Person()); }
 
    [AcceptVerbs("POST")]
    public ActionResult Edit(string unused)
    {
        Person model = new Person();
        try
        {
            if (!TryUpdateModel(model, new[] { "Name", "Age" })) // At some point it may be possible to have a custom binder that throws a BusinessRuleException if there were setter exceptions, but as of Preview 5, have to use the slightly hacky TryUpdateModel()
                throw new BusinessRuleException();
 
            // The real validation happens to be enforced by the model here, but that's no concern for the controller
            PersonRepository.SavePerson(model);
 
            // The operation completed successfully (so validation must have passed)
            return RedirectToAction("Completed");
        }
        catch (BusinessRuleException ex)
        {
            // Generate suitable localized UI feedback based on exception that was thrown
            ex.AddToModelState(ViewData.ModelState);
            // Re-render edit screen
            return View(model);
        }
    }
}

Notice the call to ex.AddToModelState(ViewData.ModelState);. I’ve implemented that as an extension method on BusinessRuleException (so as not to mix ASP.NET MVC concerns with the core BusinessRuleException class) – it simply iterates through the list of RuleViolations objects, and generates/adds some localized UI feedback to ViewData.ModelState for each violation.

I haven’t included the full source code here because it’s still all a bit proof-of-concept, but it’s not too hard to fill in the gaps and get a working implementation.

image

And what of client-side validation?

As we discussed earlier, not all rules can be translated to client-side validation code. However, since some of the simpler rules (those on the green end of the validation-businessrule spectrum) are often implemented as System.ComponentModel.DataAnnotations attributes, it would be a straightforward job to make a Html.ClientSideValidation(object model) helper that dynamically generates suitable validation JavaScript. This bit is essentially the same as what I implemented in my previous MVC validation post.

Of course, you could have a pluggable system to generate a JS validation configuration suitable for multiple client-side validation frameworks.

About setter exceptions

As mentioned earlier, sometimes it’s desirable to enforce validation rules in property setters. This gives you solid assurance that your model object can’t have invalid property values, because when *any* code (an MVC controller or model binder, or some other domain or service code) tries to manipulate the model object, they’ll never get away with setting invalid property values. This strategy applies only to simple individual property formatting rules (the green end of the spectrum).

When setters throw exceptions, MVC Preview 5′s DefaultModelBinder is clever enough to capture them one property at a time, building up a ModelState structure. Unfortunately, at present, it disregards the exception type, so you can’t use it to generate suitable UI feedback later. I’ve asked for this to be fixed – let’s be optimistic that the MVC team will give tighter support for setter exceptions in the next update.

Now, here’s an interesting twist. If we want some validation rules to be enforced in setters, and those rules are expressed as [Attributes], then wouldn’t it be cool if that logic was automatically injected into the setter by the attribute? This gets us into aspect-oriented programming territory (AOP). Unfortunately the .NET CLR doesn’t have much support for AOP. PostSharp is the best option we’ve got, but it does require a special code compilation process.

If instead of using the System.ComponentModel.DataAnnotations attributes we used custom attributes, then they could implement PostSharp’s special interfaces. Then, when you compile with PostSharp, the attribute’s simple validation logic can automatically get injected into the property setter. However, if you *don’t *compile with PostSharp, then the logic wouldn’t automatically get injected into the setter, and you’d cleanly fall back on the behavior I showed earlier in this post.

Of course, this would only be useful if ASP.NET MVC gets better support for setter exceptions.

So, would you do validation like this?

Long post… sorry! Dear readers, I’d be very pleased to hear of any feedback on this design. If there’s any consensus on how it should work, then I or someone else could work it up into a downloadable reusable set of components and classes. This would include:

  • The BusinessRuleException class that you can throw from your model layer to signal and describe arbitrary invalid user actions. It would hold a set of strongly-typed RuleViolation classes (e.g., RequiredFieldViolation, StringLengthViolation) that your controller or view can use to generate localized UI feedback
  • Support for running System.ComponentModel.DataAnnotations attributes against a model object, to produce a collection of RuleViolation objects if there were problems
  • A custom model binder that knows how to generate and throw a suitable BusinessRuleException if there were setter exceptions
  • A helper for generating client-side validation code based on System.ComponentModel.DataAnnotations attributes (perhaps supporting multiple JS validation libraries)
READ NEXT

Prevent Cross-Site Request Forgery (CSRF) using ASP.NET MVC’s AntiForgeryToken() helper

Update: Since the Release Candidate of ASP.NET MVC, these anti-forgery helpers have been promoted to be included in the core ASP.NET MVC package (and not in the Futures assembly).

Published Sep 1, 2008