Introduction
Exceptions are the errors that happen at runtime. Exception handling is the technique to handle this runtime error in our application code. If any error is thrown in web API that is caught, it is translated into an HTTP response with status code 500- "Internal Server Error".
There are many ways to handle the exception. In this article, I will explain these one-by-one and we will also learn when to use which exception handling method.
Using HttpResponseException
This exception class allows us to return HttpResponseMessage to the client. It returns HTTP status code that is specified in the exception Constructor.
For example, the following method of EmployeeController returns Status code 404 (not found), if employee data is not found for ID.
public Employee Get([FromODataUri] int key)
{
Employee data = context.Employees.Where(k => k.Id == key).FirstOrDefault();
if (data == null)
{
thrownewHttpResponseException(HttpStatusCode.NotFound);
}
return data;
}
We have more control over the response because we can pass the entire response message (using HttpResponseMessage) to the Constructor of HttpResponseException.
public Employee Get([FromODataUri] int key)
{
Employee data = context.Employees.Where(k => k.Id == key).FirstOrDefault();
if (data == null)
{
var response = newHttpResponseMessage(HttpStatusCode.NotFound)
{
Content = newStringContent(string.Format("No Employee found with ID = {0}", key)),
ReasonPhrase = "Employee Not Found"
};
thrownew HttpResponseException(response);
}
return data;
}
Using HttpError
CreateErrorResponse method of Request object helps us to return meaningful error code and message to the client. CreateErrorResponse creates an instance of HttpError object and returns it as HttpResponseMessage object.
public HttpResponseMessage Get([FromODataUri] int key)
{
Employee data = context.Employees.Where(k => k.Id == key).FirstOrDefault();
if (data == null)
{
string message = string.Format("No Employee found with ID = {0}", key);
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
return Request.CreateResponse(HttpStatusCode.OK, data);;
}
Here, HttpError was serialized into JSON. Here, an error is passed through the same content-negotiation. This is one advantage of using HttpError.
Using Exception Filters
Exception filters can be used to handle unhandled exceptions which are generated in Web API. The exception filter can be able to catch the unhandled exceptions in Web API. This filter is executed when an action method throws the unhandled exception. Note that exception filter does not catch HttpResponseException exception because HttpResponseException is specifically designed to return the HTTP response.
We can use exception filter whenever controller action method throws an unhandled exception that is not an HttpResponseException. This is an attribute so we can decorate both action method and controller with this. Exception filter is very similar to HandleErrorAttribute in MVC.
The code shown below helps to implement the custom exception filter.
namespace WebAPITest
{
using System.Net;
using System.Net.Http;
using System.Web.Http.Filters;
public class CustomExceptionFilter: ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContextactionExecutedContext)
{
string exceptionMessage = string.Empty;
if (actionExecutedContext.Exception.InnerException == null)
{
exceptionMessage = actionExecutedContext.Exception.Message;
}
else
{
exceptionMessage = actionExecutedContext.Exception.InnerException.Message;
}
//We can log this exception message to the file or database.
var response = newHttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = newStringContent(“An unhandled exception was thrown by service.”),
ReasonPhrase = "Internal Server Error.Please Contact your Administrator."
};
actionExecutedContext.Response = response;
}
}
}
Register Exception Filters
There are many ways to register exception filter but the developers generally follow three approaches to register filter.
- Decorate Action with exception filter.
- Decorate Controller with exception filter.
- Filter Register globally.
To apply the exception filter to the specific action, the action needs to decorate with this filter attribute. In the following example, I have applied CustomExceptionFilter filter to only one part of the action.
[CustomExceptionFilter]
public HttpResponseMessage Get([FromUri]int key)
{
...
...
...
}
To apply the exception filter to all the actions of a controller, the controller needs to be decorated with this filter attribute. I the following example, I have applied CustomExceptionFilter filter to EmployeeController class.
[CustomExceptionFilter]
public class EmployeeController : ApiController
{
...
...
...
}
To apply the exception filter to all Web API controllers, the filter needs to register to GlobalConfiguration.Configuration.Filters collection.
public static class WebApiConfig
{
public static void Register(HttpConfigurationconfig)
{
config.Filters.Add(newCustomExceptionFilter());
}
}
Following is a snapshot of Fiddler, when unhandled execution occurred in the action method. We get "An unhandled exception was thrown by service" as a response.
Using Exception Handlers
Normally, exception filter is used to catch the unhandled exception. This approach will work fine but it fails if any error is raised from outside action. For example, if any error is raised in the following area then exception filter will not work.
- Error inside the exception filter.
- Exception related to routing.
- Error inside the Message Handlers class.
- Error in Controller Constructor.
Web API 2 provides a good alternative way to achieve global exception handling. Web API provides "ExceptionHandler" abstract class to handle exception above said area.
Using the following code, we can define the custom implementation of ExceptionHandler.
namespace WebAPITest
{
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.ExceptionHandling;
using System.Web.Http.Results;
public class GlobalExceptionHandler: ExceptionHandler
{
public async override TaskHandleAsync(ExceptionHandlerContext context, CancellationTokencancellationToken)
{
// Access Exception using context.Exception;
const string errorMessage = "An unexpected error occured";
var response = context.Request.CreateResponse(HttpStatusCode.InternalServerError,
new
{
Message = errorMessage
});
response.Headers.Add("X-Error", errorMessage);
context.Result = new ResponseMessageResult(response);
}
}
}
Same as exception filter, Exception handler is also required to be registered. ExceptionHandler is inheriting from IExceptionHandler interface and Web API has already this type of class registered so we just need to replace this class to our custom exception handler class because Web API doesn’t support multiple ExceptionHandler.
public static class WebApiConfig
{
public static void Register(HttpConfigurationconfig)
{
config.Filters.Add(new CustomExceptionFilter());
config.Services.Replace(typeof(IExceptionHandler), newGlobalExceptionHandler());
}
}
Summary
Web API supports many ways of exception handling. Following is a summary of exception handling in Web API described in this article:
- We can use HttpResponseException when the possibility of exception is known by us. In the above example, we have thrown exception using HttpResponseException class as we know there is a chance to employee not found in the database.
- We can use exception filter to catch unhandled exceptions on action/controllers.
- We can use Exception Handlers to catch any type of unhandled exception application-wide.