Proper validation of input to REST APIs is important and if you also can provide good error messages in case of erronous input you will make the API a lot easer to use for the consumers of your API.
Using FluentValidation doing validation and error messages becomes very simple.
Combined with an IoC container it all can be a real pleasure to work with.
You may choose any IoC container, but the one I use these days (simply because the project I am working on has chosen it), is Castle Windsor.
A FluentValidator is a class that can look something like this:
public class CustomerValidator : AbstractValidator
{
public CustomerValidator()
{
RuleFor(customer => customer.LastName).NotEmpty();
RuleFor(customer => customer.FirstName).NotEmpty().WithMessage("Please specify a first name");
RuleFor(customer => customer.Street).Length(5, 250);
RuleFor(customer => customer.Birthdate)
.InclusiveBetween(new DateTime(1900, 1, 1), DateTime.Today.AddYears(-18));
}
}
Using a Validator like the one shown above we get a nice separation of concerns. The Controller is not cluttered with a lot of validation code and we get a very nice and readable set of validation for our inputs of type Customer. See the FluentValidation site for more details on how to write Validators.
Lets quickly go through the steps to create a Web API using FluentValidation and Castle Windsor to see how it all can fit together.
Create an Empty Web project and add nuget packages
Start with Visual Studio 2013 and do Create new project, select ASP.NET Web Application and choose the Empty template, this way we can select what we want to add, and there's not a lot to add, as it is so easy to create a REST service using Web API 2 and OWIN.
Start by adding the following nuget packages to your project:
- FluentValidation.WebApi : this will add the FluentValidation core package as well
- Microsoft.AspNet.WebApi.Owin
- Microsoft.Owin.Host.Systemweb : this package adds support for hosting your API in IIS
- Castle.Windsor
Startup
Add a class named Startup. By default OWIN will pick this class and use this to configure your WEB API.
My Startup looks like this:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented;
var container = new WindsorContainerConfigurator().Configure(config);
FluentValidationModelValidatorProvider
.Configure(config, provider => provider.ValidatorFactory = new WindsorValidatorFactory(container));
app.UseWebApi(config);
}
}
This code configures our app to use Attribute routing, then we configure how we want our JSON to look before we get to the more interesting parts.
To use IoC with Web API we need to register the Controllers and their dependencies. We also want to register the our FluentValidation Model Validators.
Finally we need to configure Web API to use the container when it needs to resolve something.
All this is done in the WindsorContainerConfigurator class.
The last step before telling the builder to use our Web Api configuration is to glue the FluentApi together with our IoC Container. This step is done with the instruction
FluentValidationModelValidatorProvider
.Configure(config, provider => provider.ValidatorFactory = new WindsorValidatorFactory(container));
The WindsorValidatorFactory
is a small class that adapts our IoC container to the FluentValidator library.
Container Configuration
There are two steps to configure the container.
First we register our classes, in Windsor this is typically done in Installers.
Then we set the WebAPI dependency resolver to our WindsorResolver.
The WindsorResolver is a class that I have copied from: https://github.com/WebApiContrib/WebApiContrib.IoC.CastleWindsor/blob/master/src/WebApiContrib.IoC.CastleWindsor/WindsorResolver.cs
There is also a nuget package of this code, but it seems to not have been updated for some time so I used the source code instead.
Here's the WindsorContainerConfigurator.
public class WindsorContainerConfigurator
{
public IWindsorContainer Configure(HttpConfiguration config)
{
var container = new WindsorContainer()
.Install(new CustomerInstaller());
config.DependencyResolver = new WindsorResolver(container);
return container;
}
}
Registering the Controllers and Validators
The Installer is shown below. It simply registers all the ApiController and all FluentValidation Validators.
As we will see this will ensure that to implement validation for an input, we simply need to implement a Validator that inherits from AbstractValidator and it will automatically be wired up and used to validate the input of any ApiController method matching the Model type of the Validator.
public class ControllerInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Classes.FromThisAssembly()
.BasedOn<ApiController>()
.WithServiceSelf()
.LifestylePerWebRequest());
container.Register(
Classes.FromThisAssembly()
.BasedOn(typeof(AbstractValidator<>))
.WithServiceFirstInterface()
.LifestylePerWebRequest());
}
}
WindsorValidatorFactory
The WindsorValidatorFactory is simple boilerplate code that integrate FluentValidation and Windsor.
We simply inherit from the FluentValidation class called ValidatorFactoryBase and override the CreateInstance method.
When FluentValidation wants to check for a validator of a model type it will call CreateInstance. We then check the container to see if there's a Validator registered for the type and return it if we find it.
It all looks like this:
public class WindsorValidatorFactory : ValidatorFactoryBase
{
private readonly IWindsorContainer _container;
public WindsorValidatorFactory(IWindsorContainer container)
{
_container = container;
}
public override IValidator CreateInstance(Type validatorType)
{
if (_container.Kernel.HasComponent(validatorType))
return _container.Resolve(validatorType) as IValidator;
return null;
}
}
And that's it, I have published a complete sample application on Github here: https://github.com/novanet/FluentValidation.WebApiDemo