While Data Annotations.aspx) approach is generally used for user input data validation, FluentValidation that I’m going to introduce in this article might be better for delelopers with more benefits. Source code used in this post can be found at:
Typical User Input Data Validation with Data Annotations
Generally, for user input validation, data annotations approach like below is used. Each property that needs to be validated is required to have attribute classes:
public class RegisterViewModel
{
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
public string ConfirmPassword { get; set; }
}
`</pre>
Attribute classes like `Required`, `StringLength` and `Compare` are responsible for data validation. If we use them, `ModelState.IsValid` value in the controller can be either `true` (valid) or `false` (invalid).
<pre>`[HttpPost]
public virtual async Task<ActionResult> Register(RegisterViewModel form)
{
var vm = form;
if (ModelState.IsValid)
{
vm.Validated = true;
}
return View(vm);
}
`</pre>
It's perfectly OK for data validation. However, personally, this looks too verbose to me. I need a single point for data validation. If you are like me, [`FluentValidation`](https://github.com/JeremySkinner/FluentValidation) will be yours. Let's change this model using `FluentValidation`
## User Input Data Validation with FluentValidation
You might need to download two packages from [NuGet](https://nuget.org):
- FluentValidation
-
Once you download both, then the
RegisterViewModelclass can be changed to:`[Validator(typeof(RegisterViewModelValidator))] public class RegisterViewModel { [Display(Name = "Email")] public string Email { get; set; } [DataType(DataType.Password)] [Display(Name = "Password")] public string Password { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm password")] public string ConfirmPassword { get; set; } } `As you can see,
Validator(typeof(RegisterViewModelValidator))has been added andRequired,StringLengthandComparehave been removed. Yes, that’s right.RegisterViewModelValidatordefines all the validation rules like:`public class RegisterViewModelValidator : AbstractValidator<RegisterViewModel> { public RegisterViewModelValidator() { RuleFor(x => x.Email) .NotNull().WithMessage("Required") .EmailAddress().WithMessage("Invalid email"); RuleFor(x => x.Password) .NotNull().WithMessage("Required") .Length(6, 100).WithMessage("Too short or too long"); RuleFor(x => x.ConfirmPassword) .NotNull().WithMessage("Required") .Equal(x => x.Password).WithMessage("Not matched"); } } `Once of benefits using
FluentValidationis that setting validation rules looks very intuitive. For example, theEmailproperty is required (Not NULL) and formatted as an email. In addition to this, each validation rule has its own error message when the validation fails. Once validators are defined,Application_Start()fromGlobal.asax.csshould callFluentValidationModelValidatorProvider.Configure()to activate those validators defined.`public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { ... FluentValidationModelValidatorProvider.Configure(); } } `Now,
ModelState.IsValidproperty still hastrueorfalseafter the validation. Let’s move on to setting up views.Setting up Views for User Input
Corresponding Razore view might look like this:
`@using (Html.BeginForm(MVC.Home.ActionNames.Register, MVC.Home.Name, FormMethod.Post))
{
<div>@Html.LabelFor(m => m.Email) <div> @Html.TextBoxFor(m => m.Email, new Dictionary<string, object>() { { "placeholder", "Email" } }) @Html.ValidationMessageFor(m => m.Email) </div></div>
<div>@Html.LabelFor(m => m.Password) <div> @Html.PasswordFor(m => m.Password, new Dictionary<string, object>() { { "placeholder", "Password" } }) @Html.ValidationMessageFor(m => m.Password) </div></div>
<div>@Html.LabelFor(m => m.ConfirmPassword) <div> @Html.PasswordFor(m => m.ConfirmPassword, new Dictionary<string, object>() { { "placeholder", "Confirm Password" } }) @Html.ValidationMessageFor(m => m.ConfirmPassword) </div></div>
<div><div> <input type="submit" name="Submit" /> </div></div>
}
`This will be rendered in a web browser like:
`<form action="/Home/Register" method="post"> <div> <label for="Email">Email</label> <div> <input data-val="true" data-val-email="Invalid email" data-val-required="Required" id="Email" name="Email" placeholder="Email" type="text" value="" /> <span class="field-validation-valid" data-valmsg-for="Email" data-valmsg-replace="true"/> </div> </div> <div> <label for="Password">Password</label> <div> <input data-val="true" data-val-length="Too short or too long" data-val-length-max="100" data-val-length-min="6" data-val-required="Required" id="Password" name="Password" placeholder="Password" type="password" /> <span class="field-validation-valid" data-valmsg-for="Password" data-valmsg-replace="true"/> </div> </div> <div> <label for="ConfirmPassword">Confirm password</label> <div> <input data-val="true" data-val-equalto="Not matched" data-val-equalto-other="*.Password" data-val-required="Required" id="ConfirmPassword" name="ConfirmPassword" placeholder="Confirm Password" type="password" /> <span class="field-validation-valid" data-valmsg-for="ConfirmPassword" data-valmsg-replace="true"/> </div> </div> <div> <div> <input type="submit" name="Submit" /> </div> </div> </form> `Therefore, the controller performs server-side validation. If you add a javascript validation library like jQuery Validation, client-side validation can also be easily developed.
Setting up IoC Container for Dependency Injection for Validation
Any validator using
FluentValidationlibrary inheritsAbstractValidator<T>which implements theIValidatorinterface. It’s great because classes implementing interfaces can easily be both unit-testable and dependency-injectable. Let’s have a look how to setup IoC containers for those validators using Autofac. First of all, changeApplication_Start()fromGlobal.asax.cslike below:`private void Application_Start(object sender, EventArgs e) { ... DependencyConfig.RegisterDependencies(); } `As you can see,
FluentValidationModelValidatorProvider.Configure()within theApplication_Start()method has been replaced withDependencyConfig.RegisterDependencies().DependencyConfigclass under theApp_Startdirectory might look like:`public static class DependencyConfig { public static void RegisterDependencies() { var builder = new ContainerBuilder(); ... RegisterValidators(builder); var container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); RegisterValidationProviders(container); } private static void RegisterValidators(ContainerBuilder builder) { builder.RegisterType<RegisterViewModelValidator>() .Keyed<IValidator>(typeof(IValidator<RegisterViewModel>)) .As<IValidator>(); } ... } `For more details to build dependencies can be found at
Autofacwebsite. Instead, we’re focusing on two private methods –RegisterValidators()andRegisterValidationProviders(). All validator classes should go into theRegisterValidators()method for IoC registration. TheRegisterValidationProviders()method actually activates validators implemented byFluentValidationlibrary like:`private static void RegisterValidationProviders(IContainer container) { ModelValidatorProviders.Providers.Clear(); #1 DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false; #2 var fvmvp = new FluentValidationModelValidatorProvider(new ValidatorFactory(container)) { AddImplicitRequiredValidator = false, }; #3 ModelValidatorProviders.Providers.Add(fvmvp); #4 } ` #1: There might be existing validator providers within memory. This clears the memory first.#2: This explicitly declares to performDataAnnotationsmodel validation.#3: This creates validators provided byFluentValidationusingValidatorFactoryclass. This also explicitly declares to perform validations.#4: This adds validators usingFluentValidationlibrary.There might be many validator classes inheriting
AbstractValidator<T>for each model. Therefore, all registered validators intoAutofacshould be instantiated by thisValidatorFactoryclass. Here’s the details:`public class ValidatorFactory : ValidatorFactoryBase { private readonly IContainer container; public ValidatorFactory(IContainer container) { this.container = container; } public override IValidator CreateInstance(Type validatorType) { var validator = container.ResolveOptionalKeyed<IValidator>(validatorType); return validator; } } `As stated above, this is possible because all validators implements
IValidatorinterface. Once we completes this step, we should remove all[Validator(typeof(TValidator))]attribute classes on models. For this case, it will be[Validator(typeof(RegisterViewModelValidator))].Unit Testing Validators
As
IValidatorinterface gives us much flexibility, we can also perform unit tests. With NUnit and FluentAssertions, we can easily write unit tests.`[TestFixture] public class RegisterModelValidatorTest { private IValidator _validator; [SetUp] public void Init() { this._validator = new RegisterViewModelValidator(); } [Test] [TestCase("email", "password", "password", false)] [TestCase(null, "password", "password", false)] [TestCase("e@mail.com", "password", "password", true)] public void RegisterViewModel_Should_Be_Validated(string email, string password, string confirmPassword, bool expected) { var vm = new RegisterViewModel() { Email = email, Password = password, ConfirmPassword = confirmPassword, }; var result = this._validator.Validate(vm); result.IsValid.Should().Be(expected); } `Any validator implementing the
IValidatorinterface has theValidate()method that actually performs the validation. Like above, the model is validated and tested. However, this conducts validation for whole properties in the model. If I want to test only one property, what can I do?FluentValidationprovides a few helper methods for this case.ShouldHaveValidationErrorFor()ShouldNotHaveValidationErrorFor()ShouldHaveChildValidator()Therefore, testing one property can be written like:
`[Test] [TestCase("password", false)] [TestCase(null, true)] public void Password_Should_Be_Validated(string password, bool exceptionExpected) { var validator = this._validator as RegisterViewModelValidator; try { validator.ShouldNotHaveValidationErrorFor(p => p.Password, password); } catch (ValidationTestException ex) { if (exceptionExpected) { Assert.Pass(); } else { Assert.Fail(ex.Message); } } catch (Exception ex) { Assert.Fail(ex.Message); } } `The
Passwordproperty of theRegisterViewModelmodel has a validation rule inRegisterViewModelValidatorclass. Hence, it will be tested. The helper method,ShouldNotHaveValidationErrorFor(), will be passed, if the validation succeeds. However, if the validation fails, it will throw theValidationTestExceptionexception. If this is expected, it passes the test; otherwise it fails the test. Likewise, theConfirmPasswordproperty can be tested like below:`[Test]
[TestCase(“password”, “password”, false)]
[TestCase(“password”, “different”, true)]
public void ConfirmPassword_Should_Be_Validated(string password, string confirmPassword, bool exceptionExpected)
{
var validator = this._validator as RegisterViewModelValidator;
try
{validator.ShouldNotHaveValidationErrorFor( p => p.ConfirmPassword, new RegisterViewModel() { Password = password, ConfirmPassword = confirmPassword });}
catch (ValidationTestException ex)
{if (exceptionExpected) { Assert.Pass(); } else { Assert.Fail(ex.Message); }}
catch (Exception ex)
{Assert.Fail(ex.Message);}
}
So far, we’ve take a brief look at using FluentValidation library for our ASP.NET MVC web app. Instead of scattering those validation rules all over the places, we can place them into one spot so that we can get benefits, in terms of maintainablilty.
One of downsides using FluentValidation is that it supports Web API with many limitations. Of course, we can validate models through Web API with many walkarounds. I don’t think, however, it’s efficient. According to the good news from the library creator/maintainer, Jeremy Skinner, he has been focusing on ASP.NET MVC 6 with new MVC/Web API features. So, we hope the next version of FluentValidation will fully support both MVC and Web API.