Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validating model properties WCF Web APi

I have a set of services hosted with WCF Web Api, what I need to do is validate the properties inside the models of the app.

In MVC 3 for example I decorate properties in the model like this:

    [StringLength(30)]
    public string UserName { get; set; }

and then in the controller I proceed like this to verify os the model has met the validation parameters:

    [HttpPost]
    ActionResult Create(Model myModel)
    { 
        if(ModelState.IsValid(){
           Post the model
        }
        else
        {
           Don't post the model
        }
    }

Is there a way to do something similar in WCF Web Api?

like image 227
Daniel Avatar asked Nov 01 '11 23:11

Daniel


1 Answers

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!!

like image 102
Daniel Avatar answered Sep 20 '22 03:09

Daniel