random .NET and web development musings

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 :)

3 COMMENTS
Scott Littlewood
May 7, 2010
ad

Great article!

I’ve been following OpenRasta for a while now and love seeing all the great ways it can be extended. Code samples and documentation are lacking but I’m beginning to see more and more of these :)

Keep up the great posts!

May 7, 2010
ad

Thanks! I hope to get some more posts up soon :)

sync
September 27, 2010
ad

Hi! Great post.
But have one stupid question:

You wrote
// add validation errors here
Please advise, where should i store errors?

Thanks.

Post a comment