Ok I finally managed to get validations for my models working. I wrote a validation handler and a couple of extensions methods. First thing the validation handler:
public class ValidationHandler<T> : HttpOperationHandler
{
private readonly HttpOperationDescription _httpOperationDescription;
public ValidationHandler(HttpOperationDescription httpOperationDescription)
{
_httpOperationDescription = httpOperationDescription;
}
protected override IEnumerable<HttpParameter> OnGetInputParameters()
{
return _httpOperationDescription.InputParameters
.Where(prm => prm.ParameterType == typeof(T));
}
protected override IEnumerable<HttpParameter> OnGetOutputParameters()
{
return _httpOperationDescription.InputParameters
.Where(prm => prm.ParameterType == typeof(T));
}
protected override object[] OnHandle(object[] input)
{
var model = input[0];
var validationResults = new List<ValidationResult>();
var context = new ValidationContext(model, null, null);
Validator.TryValidateObject(model, context, validationResults,true);
if (validationResults.Count == 0)
{
return input;
}
else
{
var response = new HttpResponseMessage()
{
Content = new StringContent("Model Error"),
StatusCode = HttpStatusCode.BadRequest
};
throw new HttpResponseException(response);
}
}
}
Notice how the Handler receives a T object, this is mainly because I would like to validate all the model types within the API. So the OnGetInputParameters specifies that the handler needs to receive a T type object, and the OnGetOutputParameters specifies that the handler needs to return an object with the same T type in case validations policies are met, if not, see how the on handle method throws an exception letting the client know that there's been a validation problem.
Now I need to register the handler, for this I wrote a couple of extensions method, following an example of a Pedro Felix's blog http://pfelix.wordpress.com/2011/09/24/wcf-web-apicustom-parameter-conversion/ (this blog helped me a lot, there are some nice explanations about the whole handler operations thing). So these are the extensions methods:
public static WebApiConfiguration ModelValidationFor<T>(this WebApiConfiguration conf)
{
conf.AddRequestHandlers((coll, ep, desc) =>
{
if (desc.InputParameters.Any(p => p.ParameterType == typeof(T)))
{
coll.Add(new ValidationHandler<T>(desc));
}
});
return conf;
}
so this methos checks if there is a T type parameter in the operations, and if so, it adds the handler to that specific operation.
This one calls the other extension method AddRequestHandler, and that method add the new handler without removing the previous registered ones, if the exist.
public static WebApiConfiguration AddRequestHandlers(
this WebApiConfiguration conf,
Action<Collection<HttpOperationHandler>,ServiceEndpoint,HttpOperationDescription> requestHandlerDelegate)
{
var old = conf.RequestHandlers;
conf.RequestHandlers = old == null ? requestHandlerDelegate :
(coll, ep, desc) =>
{
old(coll, ep, desc);
};
return conf;
}
The last thing is to register the handler:
var config = new WebApiConfiguration();
config.ModelValidationFor<T>(); //Instead of passing a T object pass the object you want to validate
routes.SetDefaultHttpConfiguration(config);
routes.MapServiceRoute<YourResourceObject>("SomeRoute");
So this is it.. Hope it helps somebody else!!