Web API Deep Dive - HTTP Error Codes from Exceptions (Part 3 of 6)

Microsoft’s ASP.Net Web API 2.2 allows you to easily create REST style APIs on an IIS website. Microsoft has some great documentation on how to get started with it, so I won’t rehash that here. Instead, I’m going to go a little deeper into some powerful features that can be used with Web API.

HTTP Error Codes from Exceptions

In the last post, we looked at how you can specify HTTP Response codes to return from your API controller when everything is working properly. However, sometimes a problem will occur deep inside your code. Continuing down the REST path, when problems occur, you should use the semantics of HTTP by returning an HTTP status code that reflects the error. When problems occur in your code, exceptions are typically thrown. This week we will be looking at how we can specify the HTTP Response code and content to return depending on the exception that is thrown.

Feb 10, 2016 Update - Fixed bug in second code block

This is a matter of style, but the first thing that I like to do is to create a custom exception, so that I know that what I’m doing will only apply when I specifically raise my custom exception.

1
2
3
4
5
6
7
8
9
10
11
public class OrderTotalWrongException : Exception
{
public OrderTotalWrongException(int orderId, decimal totalOrderAmount,
string message) : base(message)

{

this.OrderId = orderId;
this.TotalOrderAmount = totalOrderAmount;
}
public int OrderId { get; set; }
public decimal TotalOrderAmount { get; set; }
}

Next, create an ExceptionFilterAttribute. An ExceptionFilterAttribute allows you to handle a exception that is not handled by a controller action. Please note that this attribute can be used with multiple exception types you want to use by adding an ‘else‘ statement after the first ‘if‘ statement.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class OrderingExceptionResponseAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{

if (context.Exception is OrderTotalWrongException)
{
var response= new HttpResponseMessage(HttpStatusCode.Conflict);
response.ReasonPhrase = "Problem with Order Total";
var e = (OrderTotalWrongException) context.Exception;
response.Content = new StringContent(
"Problem found with order " + e.OrderId +
". It showed up with a total of " + TotalOrderAmount);
context.Response = response;
}
}
}

Let’s dig into this code. We’re creating an Exception Filter. This is an attribute that, when applied to a controller or action method, will give this code a chance to handle unexpected exceptions.

We only care about the specific custom exception I created earlier. When it is thrown, we create a HttpResponseMessage - this is the response that will be sent to the client that called our API. Our normal controller and action method can’t help us, because they weren’t able to handle this exception, so it’s up to us to construct a meaningful response. We set the HTTP Status Code - in this case we’re using HTTP 409, Conflict, because there is a conflict with the total amount of the order. http://httpstatus.es has a helpful listing of the available HTTP status codes. I then add some textual description of the error that is built from the properties on the custom Exception type we created earlier, and we set the response on the context object so that the HttpResponseMessage we created will be returned to the client.

Now you have created the attribute that can be added to any controller or action method. Next, you can either manually add it to the controllers you want to add it to, or you can add it to all controllers, all actions, by opening up App_Start\WebApiConfig.cs and adding this line:

1
config.Filters.Add(new OrderingExceptionResponseAttribute ());

You have now wired up a global exception handler that will kick in and provide a specific HTTP response when an action is called, the custom exception is thrown, and you don’t handle the exception in the action. The nice part about this is that your actions and anything that your actions call don’t need to know that this is in place. All they have to do is throw your custom exception.

1
2
3
4
5
6
7
8
9
10
11
12
13
[HttpPut, Route("Order/{orderId}")]
public IHttpActionResult UpdateOrder(int orderId, UpdateOrderVM updateOrder)
{

if (!orders.OrderExists(orderId))
{
return new NotFoundResult(Request);
}

// The UpdateOrder method has logic that
// might throw OrderTotalWrongException
orders.UpdateOrder(updateOrder);
return Ok();
}

References:
http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling
http://stackoverflow.com/questions/15167927/how-do-i-log-all-exceptions-globally-for-a-c-sharp-mvc4-webapi-app
http://www.apress.com/9781430259800
https://github.com/filipw/apress-recipes-webapi/tree/master/Chapter%2007/7-1
http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling