Web API Global Exception Handler using an ExceptionFilterAttribute filter

W

Web API and MVC with C# offer stellar global filters that can be registered in the App_Start/FilterConfig.cs file. For Web API and MVC the global filters goes inside the RegisterGlobalFilters function. This function contains an override based on whether you are applying the filter to MVC or Web API. Today we are going to look at how to implement an extension filter of the ExceptionFilterAttribute.

Understanding what it means handling exceptions

Exceptions inside your code typically occur in the following scenarios:

  1. Your code throws a specific exception with an explicit human-readable message that you would want to display the user to understand went wrong.
  2. Your code threw an unexpected exception that you did not handle inside a try/catch function.

I think it is a code idea to handle this somewhere to return a proper error code to the user with a user-friendly friendly. Also a good opportunity to obscure sensitive data like a database error with details about your database tables.

Enter the ExceptionFilterAttribute

Creating the OnExceptionAttribute extending the ExceptionFilterAttribue

To get start I am going to create a file called OnExceptionAttribute. I place this file inside the App_Start folder. Here is a stubbed out file that will be filled in shortly.

[code]
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Net.Http;
using System.Web.Http.Filters;

namespace Api
{
public class OnExceptionAttribute : ExceptionFilterAttribute
{

public override void OnException(HttpActionExecutedContext context)
{

}

}
}
[/code]

The class OnExceptionAttribute extends the built-in ExceptionFilterAttribute class. The next part is I create a function called OnException that overrides the main function in the ExceptionFilterAttribute. This will be expanded shortly.

Before this code will be executed I know to update the previously file App_Start/FilterConfig.cs. The new filter will be added to the HttpFilterCollection. Here is an updated file:

[code]
using System;
using System.Configuration;
using System.Web.Mvc;

namespace Api
{
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{

}

///

/// These filters are for WebApi controllers
///

/// public static void RegisterGlobalFilters(System.Web.Http.Filters.HttpFilterCollection filters)
{
filters.Add(new OnExceptionAttribute());
}
}
}
[/code]

Now when any exception occurs in our Web API our OnExceptionAttribute will be executed.

Let’s now expand the OnExceptionAttribute function OnException to start handling a few exceptions and returning friendly HTTP Status Codes and response messages.

[code]
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Net.Http;
using System.Web.Http.Filters;

namespace Api
{
public class OnExceptionAttribute : ExceptionFilterAttribute
{

public override void OnException(HttpActionExecutedContext context)
{
var exceptionType = context.Exception.GetType().ToString();
var exception = context.Exception.InnerException ?? context.Exception;

HttpResponseMessage httpResponseMessage;

switch (exceptionType)
{
case “System.Data.DuplicateNameException”:

httpResponseMessage = CreateHttpResponseMessage(context.Request,
HttpStatusCode.BadRequest, context.Exception);
break;

case “System.Data.ObjectNotFoundException”:
case “System.Data.Entity.Core.ObjectNotFoundException”:

httpResponseMessage = CreateHttpResponseMessage(context.Request,
HttpStatusCode.NotFound, context.Exception);
break;

case “System.UnauthorizedAccessException”:

httpResponseMessage = CreateHttpResponseMessage(context.Request,
HttpStatusCode.Forbidden, context.Exception);
break;

case “System.ComponentModel.DataAnnotations.ValidationException”:

httpResponseMessage = CreateHttpResponseMessage(context.Request,
HttpStatusCode.BadRequest, context.Exception as ValidationException);
break;

case “System.Data.Entity.Infrastructure.DbUpdateException”:

httpResponseMessage = CreateHttpResponseMessage(context.Request,
HttpStatusCode.InternalServerError, “An error occurred, please try again.”);
break;

case “System.Data.Entity.Core.OptimisticConcurrencyException”:
case “System.Data.OptimisticConcurrencyException”:

httpResponseMessage = CreateHttpResponseMessage(context.Request,
HttpStatusCode.InternalServerError, “An error occurred, please try again. If error persists contact support.”);
break;

default:

httpResponseMessage = CreateHttpResponseMessage(context.Request,
HttpStatusCode.InternalServerError, context.Exception);
break;
}

context.Response = httpResponseMessage;
}

public HttpResponseMessage CreateHttpResponseMessage(HttpRequestMessage request, HttpStatusCode statusCode, string error)
{
return request.CreateResponse(statusCode, error);
}
}
}
[/code]

There is a lot of code in the above example, but here are the key things happening:

  1. Get the exception type.
  2. Get the exception; attempts to ensure the InnerException is used.
  3. Create a variable to store the HttpResponseMessage that will be set at the end of the function.
  4. A big switch statement to handle the various exception types differently.
  5. A case statement for a variety of different exception types.
  6. Based on the exception type set different HttpStatusCode. E.g. HttpStatusCode.NotFound when a particular object was not found while searching a database.
  7. Call a private function CreateHttpResponseMessage that returns a HttpResponseMessage
  8. Set the Response on the HttpActionExecutedContext

In my example a return a mix of HttpStatusCode to help developers how to handle a different response status code. I like to use the following statements to help you define what status code to send back:

  • 4xx – You effed up. Fix your request and try again.
  • 5xx – We effed up. Perhaps try again later or Google the error for help.

That’s it.

About the author

By Jamie

My Books