[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):
- Have a model-based validation mechanism
- Have the least amount of repetition (DRY)
- Client side validation using jQuery
- 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

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]](http://www.emadibrahim.com/wp-content/uploads/2008/09/clip-image0014-thumb.png)
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:
- I love using model attributes to define validation rules
- I like that I only need 2 lines in my view
- 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
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?