Search code examples
c#asp.netasp.net-web-apihttpcontextasp.net-web-api2

Sending overwriting response on HttpContext.Current is not a good idea?


I have a service where people use oData in order to filter data. Sample string goes like so $orderby=[SSN]&$top=3. When string cannot be parsed it throws InvalidCastException and I catch it and tell the user that query cannot be parsed.

Here's the problem. With this approach I need to do try/catch on every controller action which clutters code and I don't like how it looks. Now imagine you have 5 actions in your controller and every single one of them is wrapped with the same try/catch.

Example

public IHttpActionResult Home()
{
    try
    {
        var users = db.GetCollection("users")
        .AsQueryable()
        .AsFilteredObjects(Request.RequestUri.Query); // This line may cause exception
        return Ok(users);
    }
    catch (InvalidCastException)
    {
        return BadRequest(string.Format("Query ({0}) is invalid.", Request.RequestUri.Query));
    }
}

What I did

I removed the try/catch from controllers and moved it inside the AsFilteredObjects and then fiddled with HttpContext.Current to return same response.

public static CustomJson AsFilteredObjects(this IQueryable<BsonDocument> documents, string query)
{
    try
    {
        // removed because not really relevent
        return json;
    }
    catch (InvalidCastException)
    {
        // Simulate "return BadRequest()"
        HttpContext.Current.Response.Clear();
        HttpContext.Current.Response.SubStatusCode = (int) HttpStatusCode.BadRequest;
        HttpContext.Current.Response.Output.Write("{\"Message\":\"Query (" + query + ") is invalid.\"}");
        HttpContext.Current.Response.ContentType = "application/json";
        HttpContext.Current.Response.End();
        return null;
    }
}

My question

For some reason it feels like it's bad idea. I wonder if there is somewhat more elegant way of doing this?


Solution

  • In my opinion, you should use the global ExceptionHandler, new in Web API 2.

    This link will tell you about exception handling in Web API in general and this link should help explain the global exception handling in the Web API. It allows you to throw exceptions from all over the place and catch/handle them all in one place.

    Previously, the Web API didn't have an easy way to handle errors globally. Some unhandled exceptions could be processed via exception filters, but there were a number of cases that exception filters couldn't handle. The solution was to provide a new user-replaceable service, IExceptionHandler, to deal with unhandled exceptions. It provides access to an exception context containing relevant information from the point where the exception was detected, particularly the HttpRequestMessage, the HttpRequestContext, the thrown exception and the exception source.

    By following the examples in the above links, in my project I'm now able to throw all sorts of exceptions and not worry about try/catch at the point where they're thrown (polluting my code with exception handling all over the place), as my global exception handler is now configured in one place and takes care of (almost) all possible exceptions. And because every rule has an "exception" (pun intended!), in this case it's the HttpResponseException type, which is not handled by the global exception handler.