Skip to content

Exception handling in Web API 2

REST Web APIs are becoming more and more popular and have seen a large uptake recently. One of the nicer features that is available in the .NET web API 2 toolkit is it’s inbuilt exception handling. This allows you to maintain separation on concerns and stops controllers becoming bloated beasts of burden, or logging.

A common scenario for people who have just started to build Web APIs is to blindly implement try-catch exception handling. This might look something like this

   public class AnimalController : ApiController
   {
       public IHttpActionResult Get(string name)
       {
           try
           {
               return Ok(new Context().GetAnimal(name));
           }
           catch (AnimalNotFoundException exception)
           {
               //Do some logging
               return NotFound();
           }
           catch (AnimalExtinctException exception)
           {
               //Do some different handling
               //Do some logging
               return BadRequest(exception.Message);
           }
           catch (Exception exception)
           {
               //Do some handling
               //Do some logging
               return InternalServerError();
           }
           
       }
   }

As you can see, the actual code that performs the action that we want for this controller method is only a single line. Surrounding this are multiple try catch statements that obscure the purpose of this controller method.

So what can we do to help solve this problem? Filters. These allow us to abstract all that code away from the controller. This becomes increasingly important if your controller has more methods or custom exceptions that need handling. Imagine the code above repeated four or five times!

Filters are fairly simple to use and understand. They sit in a pipeline that wraps around the API.

  1. Validation
    • Here is where the inbuilt serializer checks the object your controller method is expecting against what has been sent, and apply’s any validation required via ModelState.IsValid. I will cover this in another blog post, as it in extraneous to error handling.
  2. Filters
    • These  handle any specific errors that are thrown, and can be applied to a whole controller or individual methods.
  3. Logging
    • Again, I haven’t covered it in this post, as there is too much to cram into a single page, no matter how much you can scroll. You can however handle logging globally by creating a class that implements ExceptionLogger. Any time an exception is processed by the Web API, this class will be called with the exception in question.
  4. Exception Handling
    • Last chance saloon, anything not caught by the filters ends up here for you to manage.

In this example we are going to apply a filter to the whole controller. This means that our filter will be applied to all the methods in that controller. Individual filters can be applied to specific methods only though.

First, the code for the controller:

    [AnimalNotFoundExceptionFilter]
    public class AnimalController : ApiController
    {
        public IHttpActionResult Get(string name)
        {
            return Ok(new Context().GetAnimal(name));
        }
    }

See how much simpler that code looks compared to our first attempt? Our controller method now has a single line, which tells you exactly what it is doing.

Our filter class itself isn’t that much larger, and wraps everything up nicely.

    public class AnimalNotFoundExceptionFilterAttribute : ExceptionFilterAttribute
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            if (context.Exception is AnimalNotFoundException)
            {
                //Do some handling
                //Do some logging
 
                var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
                {
                    Content = new StringContent(context.Exception.Message),
                    ReasonPhrase = "ItemNotFound"
                };
                throw new HttpResponseException(resp);
            }
        }
    }

This filter sits in a filters file in my solution and can be mixed and matched with other filters as necessary.

The final step is to deal with any exception that hasn’t been caught by our filters. You can do this by building a global exception handler. This is last chance saloon.

    public class GlobalExceptionHandler : ExceptionHandler
    {
        public override void Handle(ExceptionHandlerContext context)
        {
           //Handle any remaining errors you could encounter in here
        }
    }

You need to remember to add the following to the WebApiConfig file in App_Start to register your global exception handler.

 config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());

As a final thought, when handling errors in a Web API it is important to always provide and return a meaningful error, so that it is possible to understand what has gone wrong. This is especially true if the error has occurred as a result of incorrect input on the users part. If the correct status code can be provided as well to give context, even better. However be sure to sensor anything that is returned. Don’t give out sensitive information in errors.

Hopefully that has given a clear idea of how to manage exceptions in Web API 2, and I will be following up with some more posts that go into logging and model validation.

Published inWeb API

Comments are closed.