In this post I’m going to discuss one method of implementing validation into your OpenRasta handlers using an OperationInterceptor.
OpenRasta deliberately doesn’t ship with a validation framework, there are plenty out there for you to use. For the purposes of this post, I’m not using any particular validation framework, simply demonstrating the point with a generic IValidator, something like:
public interface IValidator<TResource>
{
ValidationReport Validate(object resource);
}
First, lets see a way you could do validation in an MVC sort of way:
public class EditUserHandler
{
private readonly IValidator<EditUserResource> validator;
public EditUserHandler(IValidator<EditUserResource> validator)
{
this.validator = validator;
}
public OperationResult Post(EditUserResource resource)
{
var validationReport = validator.Validate(resource);
if (!validationReport.IsValid)
return new OperationResult.BadRequest {ResponseResource = resource};
// save user details here
return new OperationResult.SeeOther {RedirectLocation = "~/ "};
}
This is probably a very familiar pattern and something you could expect to see inside all hander operations which require validation. Meh. This duplication is not only wasteful, but its unnecessary noise cluttering up your Operation and we can get rid of it.
OperationInterceptors wrap the execution of an operation, giving you three places to interact, BeforeExecute, RewriteOperation and AfterExecute. In this instance, we are only concerned with BeforeExecution.
This method is called just before the Operation in invoked, this is where we want to examine the input parameters, and check if they are valid.
For the purposes of brevity, the below code assumes there is only one parameter to your operation method; hence the FirstOrDefault();
public class ValidationOperationInterceptor : OperationInterceptor
{
private readonly IDependencyResolver resolver;
private readonly ICommunicationContext context;
public ValidationOperationInterceptor(IDependencyResolver resolver, ICommunicationContext context)
{
this.resolver = resolver;
this.context = context;
}
public override bool BeforeExecute(OpenRasta.OperationModel.IOperation operation)
{
var input = operation.Inputs.FirstOrDefault();
if(input == null)
return true;
var parameter = input.Binder.BuildObject();
var validatorType = typeof(IValidator<>).MakeGenericType(parameter.Instance.GetType());
if (!resolver.HasDependency(validatorType))
return true;
var validator = this.resolver.Resolve(validatorType) as IValidator;
var validationReport = validator.Validate(parameter.Instance);
if (validationReport.IsValid)
return true;
// add validation errors here
context.OperationResult = new OperationResult.BadRequest { ResponseResource = parameter.Instance };
return false;
}
}
We register this OperationInterceptor as follows:
ResourceSpace.Uses.CustomDependency<IOperationInterceptor, ValidationOperationInterceptor>(DependencyLifetime.Transient);
The above code assumes that the parameter binding succeeded, I’ll demonstrate a way to deal with binding failures in a future post (its very similar to the above code).
So what effect does this code have? Well, it can make your handlers super clean:
public class EditUserHandler
{
public OperationResult Post(EditUserResource resource)
{
// save user details here
return new OperationResult.SeeOther {RedirectLocation = "~/ "};
}
Your Operations are now only called when the resource is valid, excellent!
You may be seeing one obvious drawback with this approach, that being what if your resources need extra data setting on them before they get sent to the codec in the case of invalidity? When editing a user, perhaps the resource needs to also contain a list of possible authorization roles, one of which will be selected for the user. Having a single mechanism for handling validation for all types doesn’t allow you anywhere to set this extra information on the resource. Well, there is a solution I’ll be proposing in my next post.
So stay tuned