What I really appreciate in ASP.NET MVC and WebAPI are the binders and its validation. It’s really cool to have a class with all parameter and, for those who have the data annotations, the automatic validation.
In this article, I’m going to “speak” about the validation, probably one of the most boring stuff a developer needs do during the programming.
That’s what I mean with “boring stuff”
public class UserService
{
public void CreateUser(string username, string email, string website)
{
if(string.IsNullOrEmpty(username))
{
throw new ArgumentException("The username must contain a valida value");
}
if(string.IsNullOrEmpty(email))
{
throw new ArgumentException("The email must contain a valida value");
}
if(!Regex.IsMatch(email,"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$"))
{
throw new ArgumentException("The specified email is not valid.");
}
if(!string.IsNullOrEmpty(website) && !Regex.IsMatch(website,"^http(s)?://([\w-]+.)+[\w-]+(/[\w- ./?%&=])?$"))
{
throw new ArgumentException("The specified website is not valid.");
}
//DO YOUR CODE
}
}
As you can see in the method above there are four parameters and I need to validate three of them before starting to do anything (in the example website is validated only if it has a value). Probably you, and also me, wrote this code thousand and thousand times …. ok It’s time to remove that.
My idea is to use the Data Annotation Attributes for the parameters and, using the AOP (Aspect Oriented Programming) and Interceptor Pattern, run automatically the validation for all ours layer.
Probably all of you use a Dependency Injection Framework (if you don’t use it, you have to) like Castle Windsor, Ninject, Unity and so on. All of these offers the opportunity to inject some code before calling a method for a registered service (Interceptor Pattern). In my example I’m using Castle (I love it), but the code it’s easy to move to another framework.
Stop speaking and let’s start coding.
The first thing to do is to understand how to run manually the validation: K. Scott Allen wrote a good article here, so I’m starting from this.
Now we have to create a container class for all the parameters and to add the Data Annotations. That’s important because we can’t use the Data Annotations directly on the parameter. If you did everything correct, your class now should looks like this:
public class CreateUserRequest
{
[Required]
public string Username { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Url]
public string Website { get; set; }
}
Because a method could have more than one parameter, and we don’t really need to validate all of these, it could be important to tell to the validation service (our interceptor) what needs to be validated and what doesn’t. For this reason I’ve created a custom attribute, ValidateAttribute:
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public class ValidateAttribute : Attribute
{
}
No comments for this code, it is used only like a marker.
Now change the signature of your method using the attribute and the input class like this:
public class UserService
{
public void CreateUser([Validate] CreateUserRequest request)
{
//DO SOMETHING
}
}
Good, everything is ready except for the interceptor, it’s time to create it:
public class ValidationInterceptor : IInterceptor
{
private readonly IObjectValidator validator;
public ValidationInterceptor() : this(new DataAnnotationsValidator())
{
}
public ValidationInterceptor(IObjectValidator validator)
{
this.validator = validator;
}
public void Intercept(IInvocation invocation)
{
ParameterInfo[] parameters = invocation.Method.GetParameters();
for (int index = 0; index < parameters.Length; index++)
{
ParameterInfo paramInfo = parameters[index];
object[] attributes = paramInfo.GetCustomAttributes(typeof(ValidateAttribute), false);
if (attributes.Length == 0)
{
continue;
}
this.validator.Validate(invocation.Arguments[index]);
}
invocation.Proceed();
}
}
The interceptor logic is really simple. It iterates all parameters and, only for these who have my custom attribute, it calls the Validate method.
To use an interceptor is important to register it and add to your service:
IWindsorContainer container = new WindsorContainer();
container.Register(Component.For<IInterceptor>().ImplementedBy<ValidationInterceptor>());
container.Register(Component.For<UserService>()
.ImplementedBy<UserService>()
.Interceptors<ValidationInterceptor>());
That’s all, run the this code to have the validation:
private static void Main(string[] args)
{
IWindsorContainer container = new WindsorContainer();
container.InizializeAttributeValidation();
container.Register(Component.For<IUserService>()
.ImplementedBy<UserService>()
.EnableValidation());
IUserService userService = container.Resolve<IUserService>();
try
{
userService.CreateUser(new CreateUserRequest());
}
catch (ImperugoValidatorException e)
{
//Validation exception
Console.WriteLine(e.Message);
}
finally
{
Console.ReadLine();
}
}
From now, every time you call the method CreateUser (resolved by Castle), the validation will be automatically raised and, for the parameter with a wrong value, an ImperugoValidationExcpetion will throw, you can catch them and choose how to show the message.
It’s not finished yet :). If you prefer, I’ve created a NuGet Package that helps to reduce the code. There are two packages:
The first one contains only the necessary stuff to create the right interceptor; the second one is the Castle implementation (soon I’ll release also a package for Ninject, Unity and so on).The first thing to do is to install the package (Castle in this case):
Now change the Castle code like this:
IWindsorContainer container = new WindsorContainer();
container.InizializeAttributeValidation();
container.Register(Component.For<UserService>()
.ImplementedBy<UserService>()
.EnableValidation());
If you use a different Framework and you like this validation, fork the repo here, add the new Framework, and create a “pull request”.
All my code is available here.
I’m working on the next release that is really faster because include a cache strategy, so stay tuned.