MVC3 Custom 500 Error Handling Inside the MVC Pipeline

In any well designed application, 500 errors are handled. I have seen too many applications that simply throw an exception when an error occurs - at best this gives a bad user experience, but at worst it could expose security information a potential attacker could use to exploit a hole in your application and/or system.

In many applications, particularly enterprise applications, an end user experiencing an error needs to be coupled with a logging process or a notification to a support team depending on the severity of the error. In ASP.NET Web Forms, this can be achieved by using the Application_Error event in global.asax to send a notification or log an event. The user can then be redirected to an error page either by setting the appropriate  CustomErrors properties in web.config or using a redirect in the Application_Error method.

Web.Config Redirect:


    <customErrors mode="Off" defaultRedirect="~/errors/500.aspx">
      <error statusCode="500" redirect="~/errors/500.aspx"/>
    </customErrors>

Global.asax Redirect:


    void Application_Error(object sender, EventArgs e) 
    {
        
        // Code that runs when an unhandled error occurs
        Exception objErr = Server.GetLastError().GetBaseException();
        // Do logging or email notification

        Response.Redirect("/Errors/500.aspx");
    }

Custom Exception Handling


In both Web Forms and MVC3, custom exception handlers can be built which give you a good indication of exactly what a user was doing when an error occurs. A method can then throw a custom exception on error, with customised exception details then being logged or sent to the support team.

A big downside to this approach is the decoupling of the exception from details of what the user was doing EXACTLY when the error occurred. Whether create your own exception handlers or not, your 500 error page may have a form for a user to complete and send a notification to a support team. The decoupling of the application error message and further information from the user occurs due to the timings between the 500 error details and the user sending the support message. This can make troubleshooting and/or problem solving very difficult if you need to know exactly what the user was doing at the time the error occurred - especially important if an error is only thrown when a user enters certain information in a form (for example).

The MVC3 Approach


With MVC3, 500 error handling can be done completely within the MVC pipeline. To do this requires a few steps:


Global.asax


There is a new method in global.asax called RegisterGlobalFilters, and this adds new global filters to your application. Global filters are applied to all actions within your MVC3 application and for this post I am going to add a custom exception hander filter:

        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute
            {
                ExceptionType = typeof(CustomError1),
                // CustomError1.cshtml is a view in the Shared folder.
                View = "CustomError1",
                Order = 1
            });
            filters.Add(new HandleErrorAttribute());
        }

The exception type is set to a custom exception handler I have created:

    [Serializable]
    public class CustomError1 : Exception
    {
        public CustomError1()
            : base("Custom error one has been thrown.") { }
    }

The view is a view in the Shared folder, called CustomError1. This view can only have one model, which is the System.Web.Mvc.HandleErrorInfo model:


CustomError1.cshtml


@model System.Web.Mvc.HandleErrorInfo

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <title>CustomError1</title>
</head>
<body>
    <div>
        @Model.Exception.Message
    </div>
</body>
</html>

You will also need to add the following to your web.config file:

<customErrors mode="On"></customErrors>

As you can see, details of the custom exception are available in the model for the CustomError1 view. In this view, I can then create a form which captures user input about the error, combines that with the exception from the model and send a notification/log an event that has all the information needed to troubleshoot in one place.

I hope this post is useful in detailing the power of MVC3 and exception handling, and how this can be used to couple together MVC3 exceptions with user information - your support teams will love you for this!



Comments

Popular posts from this blog

Connecting to SQL Azure with Dynamic IP Addresses

HTML to PDF Conversion in MVC 4

WebAPI and Subscriber Authentication by Custom HTTP Headers