Custom HandleErrorAttribute in ASP.NET MVC

written in mvc

I’ve been working on upgrading a WebForms application to ASP.NET MVC for several months now at work. I finally took some time to look into custom error pages and ended up needing more support than what came “out-of-the-box” with ASP.NET MVC 2.

We have a base controller that all of our app’s controllers inherit from. We’ve had a [HandleError] attribute on this base controller for a while, but for some reason we never saw our custom error page that was located in ~/Views/Shared/Error.aspx. I thought maybe I wasn’t using the attribute correctly, but it turned out that there was an error rendering the Error.aspx View!

Our app relies on a Master Page that itself relies on a ViewDataDictionary that is always populated by our base controller. To sum up the problem, the HandleErrorAttribute creates a new ViewDataDictionary for the Error View, and because our Master Page needed the ViewData created by the base controller, a null reference exception occurred when accessing properties on ViewData[“MyKey”].

If you run into this problem, you can write a custom HandleErrorAttribute that will fix the problem. I haven’t tested this much at all, as I eventually created a custom MasterPageView base that eliminates the dependency on the ViewDataDictionary. But here it is:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class HandleErrorWithPersistentViewDataAttribute : HandleErrorAttribute {
  public override void OnException(ExceptionContext filterContext) {
    base.OnException(filterContext);

    var typedResult = filterContext.Result as ViewResult;
    if (typedResult != null) {
      var tmpModel = typedResult.ViewData.Model;
      typedResult.ViewData = filterContext.Controller.ViewData;
      typedResult.ViewData.Model = tmpModel;
      filterContext.Result = typedResult;
    }
  }
}

It’s pretty simple (and probably not thought out well enough), but it worked for me. We do need to keep the Model created by the default HandleErrorAttribute which is an object of type HandleErrorInfo, which can be utilized by your Error View.

Another thing that can be done with a HandleErrorAttribute is error logging. It’s pretty simple as well. Just inherit from the HandleErrorAttribute, run the base method, grab an instance of your logger, and log it!

1
2
3
4
5
6
7
8
public class HandleErrorAndLogItAttribute : HandleErrorAttribute {
  public override void OnException(ExceptionContext filterContext) {
    base.OnException(filterContext);

    var controller = (BaseController)filterContext.Controller;
    controller.Log.Error(filterContext.Exception.Message, filterContext.Exception);
  }
}

Comments