Error handling in ASP.NET MVC Part 1: Our options
•Posts in this series
- Error handling in ASP.NET MVC Part 1: Our options (This post)
- Error handling in ASP.NET MVC Part 2: Our implementation
A lot of my work is built on Microsoft’s ASP.NET MVC framework. We wanted a standardised way to approach error handling across our web applications.
We had a couple of requirements
- Super easy to implement
- Covers the entire ASP.NET request pipeline
- See friendly error messages in production and stack traces in development
- Respects HTTP status codes - we do not want the site returning a 302 redirect on error, that should be a 500 or something
- Able to log exceptions
This is the first post in a two part series, in this part we will look at the options available to us in ASP.NET MVC.
Exception Filters
Exception filters implement the IExceptionFilter interface and are applied as attributes on either actions or controllers. They catch exceptions that occur during the MVC pipeline like errors in filters, actions, or views. In addition to being applied as an attribute, you can register an ExceptionFilter as a global filter in MVC 3 or 4. By default ASP.NET’s HandleErrorAttribute is registered.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
By registering the filter as a global filter you no longer have to decorate every controller with an exception filter to manage exceptions.
ASP.NET MVC comes with an implementation of IExceptionFilter called HandleError.
HandleError is a great way to quickly get custom error pages working. But is not suitable for our requirements as it does not support logging.
Controller OnException method
Another option is to create a base controller for all your controllers that overrides the OnException method and do all your error handling there.
I’m not a fan of this option as you need to remember to inherit from your base controller for every controller you create. This goes against my first goal of making everything super easy to implement.
Global.aspx Application_Error
Application_Error will catch all unhandled ASP.NET errors that occur while a request is been processed. That includes errors outside of the scope of exception filters such as a 404 error. However once execution reaches this point we are out of the MVC context so it’s harder to get contextual information about the request.
The approach we took
We went with a hybrid approach and used both a custom exception filter and the Application_Error event.
We use the custom exception filter that is registered globally to capture all exceptions that happen while processing the action and view. This allows us to do highly detailed logging that records a lot of contextual information about the user.
We use the Application_Error method as a fall back to catch any additional errors that occur in our application that may be outside of the exception filters’s scope. The most common error we handle at this level is a 404 Page Not Found exception. This allows up to display a custom 404 page with the correct status code.
How this meets our goals
Super easy to implement: Filters are registered globally so there is no per-controller configuration that’s required.
Covers the entire request pipeline: The Application_Error method ensures we cover the entire ASP.NET request processing pipeline however we capture additional information when we’re in the MVC context with our custom exception filter.
See friendly error messages in production and stack traces in development: Our custom error filter and the Application_Error event will respect the customErrors property in the web.config
Respects HTTP status codes: We are able to send back the correct HTTP status codes with the custom error page rather than generic 302 which redirects to a static error page.
Able to log exceptions: Our custom exception filter and Application_Error implementation will log exceptions.