Wednesday, September 17, 2014

Perform Model Validation on a Web API OData v4.0 RESTful with ODataController for HTTP PATCH

In this article we perform Model Validation on a Web API OData v4.0 RESTful with ODataController for HTTP PATCH requests. Here you will see how to fix an ODataController whose PATCH (or PUT) method is not working properly, meaning that the Delta object send inside the request body is always considered a valid Model, even if it does not comply with the Model's Data Annotations.
The automatic server side Model validation will not apply, because we got a "Delta" object, that means, a PARTIAL object, which by definition is not a valid Model (alas!, it is partial!!! (so it has for example required fields missing))

Let's see for example a PARTIAL update using the  HTTP PATCH verb. We have written a Patch() Action Method as follows. To make the validation of the object, we do not use the "IsValid" property of the ModelState, but we validate manually the received entity, using a BodyModelValidator :
Perform Model Validation on a Web API OData v4.0 RESTful with ODataController for HTTP PATCH



(COPY-PASTE THIS CODE) : 


 [EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]        public IHttpActionResult Patch([FromBody] Delta<Note> note)        {            if (note == null)            {                return StatusCode(HttpStatusCode.BadRequest);            }            Note original = Repository.Get(note.GetEntity().ID);            note.Patch(original);
            string errors = String.Empty;            var modelValidator = Configuration.Services.GetBodyModelValidator();            var metadataProvider = Configuration.Services.GetModelMetadataProvider();            bool isValid = modelValidator.Validate(original, typeof(Note), metadataProvider, this.ActionContext, String.Empty);            if (!isValid)            {                errors = this.ModelState.Keys.SelectMany(key => this.ModelState[key].Errors)                        .Select(error => error.ErrorMessage).Aggregate((i, j) => i + ", " + j);            }
            return isValid ? StatusCode(HttpStatusCode.OK) as IHttpActionResult :                  BadRequest(errors);        }


After we get the entity to update from database, we call the Patch() method, which takes care of performing  the required update of the modified properties:


Then, comes the validation code: usually held by MVC, is resumed as the "IsValid" boolean property of the ModelState class. We instead validate the entity manually inside the code, as follows:
We are using the BodyModelValidator's Validate() method together with a ModelMetadataProvider, to check if the Delta object that we got is valid, and complains with the Data Annotations. If it don't, we concatenate in a string the errors found, and render a BadRequest status response together with the errors found:


Now in order to test our ODataController, we will send an HTTP PATCH request using Fiddler , containing  a JSON entity in the Body. Remember to set the "Content-Type" to JSON at the request's header:




We got a response with OK status, because our Patch() renders an IHttpActionResult using the OK (200) Status Code .
But if we send a PATCH request inconsistent with the Data Annotations that we set, the request will be rejected:



 After the request was  rejected ,   we got an 400 status code or "Bad Request" response containing the error message informing us of what was illegal input:







That's all
In this post we've seen how we perform Model Validation on a Web API OData v4.0 RESTful with ODataController for HTTP PATCH requests, fixing an ODataController whose PATCH (or PUT) method which is not working properly, considering always the Model as valid , even if it does not comply with the Model's Data Annotations.. 

Enjoy programming.....
    By Carmel Shvartzman
כתב: כרמל שוורצמן