Corey Coogan

Python, .Net, C#, ASP.NET MVC, Architecture and Design

  • Subscribe

  • Archives

  • Blog Stats

    • 97,496 hits
  • Meta

ASP.NET MVC Auto Model Validation with xVal

Posted by coreycoogan on October 30, 2009

Overview

Validation is one of those necessary evils of software development.  Every application needs it and there are about as many ways to handle it as there are opinions.  No matter how you slice it, it’s usually a pain and just plain boring.  One of the things that has helped ease that pain are the assortment of validation frameworks that are available that allow the use of Attribute decorated models to specify validation rules.  If you are like me and prefer the View Model pattern when developing your Asp.Net MVC apps, then this is a perfect fit.  Now add the well crafted xVal Validation framework with automatic client-side validation via JQuery Validation and things begin to get easier.  In this post, I’ll take this one step further by showing how to use an
Action Filter approach, which I borrowed from the Code Camp Server code base, with NHibernate Validator to automatically validate the View Model via a ValidateModelAttribute that can be placed on the controller.  I’ve applied the same code in the past using Castle Validator by using the Adapter Pattern in a couple hours.

First things first, make sure you get everything you need by getting the latest versions of xVal, JQuery Validation, NHibernate Validator and/or Castle Validator.  I will warn you that if you get either of the mentioned Validators from the source trunk, you may have to recompile the xVal Rules Providers against those bits.  I had this problem with a breaking change from NHibernate Validator version 1.2.0.1003, but fortunately I found a patch here for xVal and was able to build the source and get everything working.  If anyone needs help with that, leave a comment and I’d be glad to lend a hand.

xVal Setup

There are a few basic steps to getting xVal available to your MVC project.  This post by xVal’s creator, Steve Sanderson, is a great place to get this introductory stuff.

Step 1:  Reference xVal.dll and xVal.RuleProviders.XXX.dll.

Step 2: Add the xVal.jquery.validate.js and jquery.validate.js libraries to your project and reference them in a masterpage or view.

Step 3: Add the xVal namespaces to the web.config so you won’t have to fully qualify your calls and can take advantage of the extension methods.

<add namespace="xVal.Html"/>
<add namespace="xVal.Rules"/>

Step 4: Add the validator to the xVal Rule Providers collection in the Global.asax Application_Start event handler.

xVal.ActiveRuleProviders.Providers.Add(new NHibernateValidatorRulesProvider());

View Model Decoration

Now that we have the basics out of the way, we can start setting up our View Model for validation.  The first step is to decorate the properties on our model with the validation attributes.  In the case of NHibernate Validator, there are many to choose from, including [Email], [NotNullNotEmpty] and [Pattern].  Here’s an example of a RegisterViewModel class from the Room Parents Online project. By including a value for the Message parameter, I’ve provided a useful error message that will get displayed to the user.

[NotNullNotEmpty(Message = "Email is Required"), Email(Message = "Invalid Email"), Length( 50)]
public virtual string Email { get; set; }

[NotNullNotEmpty(Message = "Confirm Email is Required"), Email(Message = "Invalid Email"), Length( 50)]
public virtual string ConfirmEmail { get; set; }

[NotNull(Message="Please specify the best time to be contacted")]
public virtual BestTimeToContact BestTimeToContact { get; set; }

[NotNull(Message = "Please specify if you wish to be contacted through email")]
public virtual bool AllowNotification { get; set; }

[NotNull(Message = "Please specify who can see your contact info")]
public virtual UserVisibleTo UserVisibleTo { get; set; }

[NotNullNotEmpty(Message="Nickname is required"), Length(50)]
public virtual string Nickname { get; set; }

[Pattern(@"^(\d{4})?$", Message = "The area code of your phone number are not valid")]
public virtual string Phone1 { get; set; }

The View

The next thing to do is prepare the View for xVal validation. First, make sure you have the xVal and JQuery validation scripts reference, either in the particular View or the Masterpage.

Step 1: If you prefer for all your errors to be grouped together like I do, create a Validation Summary with an ID assigned:

<div id="validationSummary" >
<%= Html.ValidationSummary() %>
</div>

Step 2: Use the xVal extension method to emit the JQuery Validation script.  The sample below takes the type of View Model as a generic parameter and tells xVal what Validation Summary to use for displaying error messages.  The .AddRule methods are handy little AJAX rule executions that are new in version 1.0.  Get more details in Steve’s post.

<%= Html.ClientSideValidation&lt;S2sol.Rpo.Web.Models.Profile.RegisterModel&gt;()
.AddRule("Email", new RemoteRule((Url.Action("ValidateUniqueEmail"))))
.AddRule("Nickname", new RemoteRule((Url.Action("ValidateUniqueNickname"))))
.UseValidationSummary("validationSummary") %>

The ValidateModelAttribute Filter

We’re almost there. Now we only need to hook up the filter to validate the model automatically. First, a quick peek at what the Action method looks like that will invoke the automatic model validation via the filter:

[AcceptPost]
[ValidateModel(typeof(RegisterModel))]
[Transaction]
public virtual ActionResult Register(RegisterModel model)

Now let’s have a look at the code for the validation filter itself:

namespace S2sol.Rpo.Web.Security.Filters
{
	[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
	public class ValidateModelAttribute : ActionFilterAttribute
	{
		private readonly Type _viewModelType;
		private readonly IValidator _validatorRunner;

		public ValidateModelAttribute(Type viewModelType)
            : this(viewModelType, ServiceLocator.Current.GetInstance<IValidator>())
		{
		}

		public ValidateModelAttribute(Type viewModelType, IValidator validatorRunner)
		{
			_viewModelType = viewModelType;
			_validatorRunner = validatorRunner;
		}

		public override void OnActionExecuting(ActionExecutingContext filterContext)
		{
			object model = GetModelFromActionParameters(filterContext);

            var errors = _validatorRunner.ValidationResultsFor(model);

            //check if the model has ICompositeValidationResult
            if (model is ICompositeValidationResult)
            {
                ICompositeValidationResult validate = model as ICompositeValidationResult;
                if (!validate.IsValid())
                {
                    foreach (var err in validate.GetResults())
                        errors.Add(err);
                }
            }

			if (errors.Count > 0)
			{
				AddErrorsToModelState(filterContext, model,errors);
			}
		}

		private object GetModelFromActionParameters(ActionExecutingContext filterContext)
		{
			foreach (var kvp in filterContext.ActionParameters)
			{
				if (kvp.Value == null)
				{
					continue;
				}

				if (kvp.Value.GetType() == _viewModelType)
				{
					return kvp.Value;
				}
			}

			throw new NullReferenceException("The action parameter was null.  Check the binding prefix.");
		}

		private void AddErrorsToModelState(ControllerContext context, object model, ICollection<IValidationResult> results)
		{
                   //IValidationResult is defined in the SharpArchitecture
                   foreach (IValidationResult result in results)
                   {
                     context.Controller.ViewData.ModelState.AddModelError(result.PropertyName, result.Message);
                   }
		}

		private static string FlattenErrors(string[] errorsForProperty)
		{
			return string.Join(", ", errorsForProperty);
		}
	}
}

After it’s all done and compiled, here’s an example of a failed registration attempt with client and server side validation and the only thing I have to do is decorate my model with my chosen validation constraints and decorate my action method with the ValidateModelAttribute filter.

About these ads

4 Responses to “ASP.NET MVC Auto Model Validation with xVal”

  1. [...] ASP.NET MVC Auto Model Validation with xVal [...]

  2. [...] to VoteASP.NET MVC Auto Model Validation with xVal (10/29/2009)Thursday, October 29, 2009 from coreycooganOverview Validation is one of those necessary evils of [...]

  3. Christian said

    Where does ICompositeValidationResult come from?

Sorry, the comment form is closed at this time.

 
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: