I am finally coming to the end of a project, and I thought it would be good to write a little post on how we have managed to set up our fluent validation for our MVC project. In the start we did some research into how to go about performing validation, and found a number of recommendations. But all involved using data annotations. In this post, I will show you how I set up fluent validation, to work smoothly with my MVC 2 project. I create a custom model binder to validate view models and show how to validate a registration using fluent validation. Finally I show how to unit test the validation rules I needed for registration.
To start with, there are some problems I have with data annotations
1 – Too many annotations make your model s look ugly. Take this example .
public class Product {
public int Id { get ; set ; }
[Required]
[StringLength(10)]
public string Name { get; set; }
[Required]
public string Description { get; set; }
[DisplayName("Price")]
[Required]
[RegularExpression(@"^$?d+(.(d{2}))?$")]
public decimal UnitPrice { get; set; }
}
Now wouldn’t it be nice to have your model/entity just look like this.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal UnitPrice { get;set; }
}
2 – Complex data validation with attributes makes your code get even more ugly. Take a lot at this example showing how to achieve slightly more complexity with data annotations. Now trying to reuse and share attributes seem to make things more and more complex….
3- I think there may be a performance issue, as we need to extract the validation attributes using reflection. Now, while these are simple models, with simple validation rules, we may not notice the performance degrading, but I am sure that with numerous complex attributes, things might run a little slow. (I need to prove this though – maybe when I get time, I will write some tests – I could be wrong here, things might change in MVC 3)
So after looking at some examples, such as xVal –dead now, Entity validation with visitors and extension methods these all did the job, but I would need to write lots of helpers, what I needed was some kind of framework for validation. Then I found fluent validation.
Linking MVC with Fluent Validation
So let us look at how we set our MVC project . Firstly, I want to automate the validation, so that any errors are automically added to the models state. With some help from Jeremy I set up an customised BindAndValidate attribute. Here is a simplified attribute we started with.
1 public class BindAndValidateAttribute : CustomModelBinderAttribute, IModelBinder {
2 AttributedValidatorFactory validatorFactory = new AttributedValidatorFactory();
3
4 public override IModelBinder GetBinder() {
5 return this;
6 }
7
8 public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
9
10 var innerBinder = new DefaultModelBinder();
11 var boundInstance = innerBinder.BindModel(controllerContext, bindingContext);
12 if (boundInstance != null)
13 ValidateInstance(boundInstance, bindingContext);
14
15 return boundInstance;
16 }
17
18 void ValidateInstance(object instance, ModelBindingContext context) {
19 var validator = validatorFactory.GetValidator(context.ModelType);
20 if (validator != null)
21 {
22 var result = validator.Validate(instance);
23 result.AddToModelState(context.ModelState, "");
24 }
25 }
26 }
Now all I need to do is hook this up with my controller method. In the Sign in method, I add the attribute [BindAndValidatiate] and all I need to do is check that the model state is valid. If so, I perform the log in.
1 public class AccountController : BaseController {
2 public ActionResult Signin() {
3 return View();
4 }
5
6 [HttpPost]
7 public ActionResult Signin([BindAndValidate] ChangeEmailModel model) {
8
9 if (ModelState.IsValid)
10 PerformSignIn();
11
12 return View();
13 }
14 }
Linking MVC View Models with Fluent Validation
Lets look at how all this works. What we need to do is create out model, then create our validator. We then hook our validator to our model by adding the validator atrribute to our model. I am using a simple Register View Model here as an example.
1 [Validator(typeof(RegisterViewModelValidator))]
2 public class RegisterViewModel
3 {
4 public string Email { get; set; }
5 public string Password { get; set; }
6 public string ConfirmPassword { get; set; }
7 }
Now for the model validator. Here we have some simple rules. The password must not be empty, and it must also be a good password. The password confirmation must be the same as the password, and finally the Email must be a valid email address, and also not already exisit in our site.
1 public class RegisterViewModelValidator : AbstractValidator<RegisterViewModel> {
2 public RegisterViewModelValidator() {
3 RuleFor(reg => reg.Password)
4 .NotEmpty()
5 .WithMessage("Please provide a password")
6 .Must(BeGoodPassword)
7 .WithMessage("Password must be at least 8 characters long, and contain numbers and letters");
8
9 RuleFor(reg => reg.ConfirmPassword)
10 .Equal(reg => reg.Password)
11 .WithMessage("Passwords do not match");
12
13 RuleFor(reg => reg.Email)
14 .EmailAddress()
15 .WithMessage("Invalid Email")
16 .Must(BeUniqueEmail)
17 .WithMessage("Account already Registered");
18 }
19
20 public bool BeGoodPassword(string password) {
21 Regex regex = new Regex(@"^(?=.{8,}$)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).*");
22 return regex.IsMatch(password);
23 }
24
25 public bool BeUniqueEmail(string email) {
26 int count = Repository.GetInstance().CountOccurrencesOfEmail(email);
27 return (count == 0);
28 }
29 }
Test your validation
Now, finally for the testing, whcih is really useful, when I want to make sure that things work 100%! To keep it simepl, I am just going to test the password rule, because testing the email requires a lot more of an explination. So working from some simple examples Here: I have written three tests. Thfirst is to make sure that the password can not be null. The second is to catch a week password. The third makes sure that a strong password does not cause an error.
1 [Test]
2 public void Should_have_error_when_Password_is_null() {
3 validator.ShouldHaveValidationErrorFor(reg => reg.Password, null as string);
4 }
5
6 [Test]
7 public void Should_have_error_when_Password_is_weak() {
8 validator.ShouldHaveValidationErrorFor(reg => reg.Password, "weakpass");
9 }
10
11 [Test]
12 public void Should_not_have_error_when_Password_is_strong() {
13 Validator.ShouldNotHaveValidationErrorFor(reg => reg.Password, "SecretPassword123");
14 }
And that is it! There is quite a lot you can achieve with fluent validation, such as reusing validators on complex properties and also some useful conditions like when or unless! The reasons I like this are that it uses generics to help build clean code. There is now no need to attributes on every property I have. Also, Jeremy was also very quick to help with any questions I had. Thanks for the help Jeremy.