Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capture exception during request deserialization in WebAPI C#

I'm using WebAPI v2.2 and I am getting WebAPI to deserialise JSON onto an object using [FromBody] attribute. The target class of the deserialisation has a [OnDeserialized] attribute on an internal method, like this:

[OnDeserialized]
internal void OnDeserialisedMethod(StreamingContext context) {
    // my method code
}

I know for a fact there is a problem with the code inside this method, I've stepped through it and found it. The problem for me is that I get no exception at all. What happens is this method gets jumped out of and the exception seems to be ignored. My controller action gets called and my target object is not properly populated because this serialisation method has not been correctly executed.

My question is; how can I capture an exception that occurs during deserialisation in WebAPI?

like image 569
Anupheaus Avatar asked Mar 03 '15 15:03

Anupheaus


2 Answers

I had exactly the same problem and bookmarked your question in hope that someone would provide a solution. I thought using ModelState implied rewriting some validations in the JSON model, but it just works, in fact it's simple and very well done. I didn't have to modify the model, just the controllers.

My code from one of my controllers, StdResponse being the class used to provide the response with details if needed (in this case, for instance) :

[HttpPost]
public StdResponse Test([FromBody]StdRequest request)
{
    if (ModelState.IsValid)
    {
        //Work on the data from the request...
    }
    else
    {
        //Retrieve the exceptions raised during deserialization
        var errors = ModelState.SelectMany(v => v.Value.Errors.Select(e => e.Exception));

        List<String> messages = new List<string>();

        foreach (Exception e in errors)
        {
            messages.Add(e.GetType().ToString() + ": " + e.Message);
        }

        return new StdResponse(exchangeVersion, "null", ExecutionResponse.WithError("StdRequest invalid", messages));
    }
}
like image 39
blazcowicz Avatar answered Oct 11 '22 14:10

blazcowicz


I've written up a filter (as suggested in various comments) that checks the ModelState and throws an exception if serialization errors did occur. Beware though, that this may not contain only serialization exceptions - that could be adjusted by specifing the concrete exception type in the Select statement.

public class ValidModelsOnlyFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (actionContext.ModelState.IsValid)
        {
            base.OnActionExecuting(actionContext);
        }
        else
        {
            var exceptions = new List<Exception>();

            foreach (var state in actionContext.ModelState)
            {
                if (state.Value.Errors.Count != 0)
                {
                    exceptions.AddRange(state.Value.Errors.Select(error => error.Exception));
                }
            }

            if (exceptions.Count > 0)
                throw new AggregateException(exceptions);
        }
    }
}

I suggest binding this filter on a global scope. I really can't fathom why it should be ok to ignore deserialization exceptions.

like image 97
betelgewse Avatar answered Oct 11 '22 12:10

betelgewse