ASP.Net MVC Framework - Handling Exception by using an Attribute

Note: This is based on the pre-CTP version of the ASP.Net MVC Framework and it is in an early stage.

I got a comment on one my previous post “ASP.Net MVC Framework - Exception Handling” that the Rescue style MonoRail is a nice solution to handle Exception thrown within a Controller. In this post I have added something similar to as the Rescue feature of MonoRail (at least I think so ;)) where an attribute can be added to a Controller or on an Action. The attribute can be used to specify which View that should be rendered when an exception is thrown. We can also specify different Views based on different exceptions. Something I also added to the code is to specify something I will call an ExceptionHanlder. The ExceptionHandler is a pre handler which will be executed before the specified exception View should be rendered. The idea of the ExceptionHandler is to log exception etc.

By using an Attribute (ExceptionHandlerAttribute) on a Controller or an Action method, we don’t need to override the OnError method of the base Controller class. I made this solution simple by only creating my own custom Controller class (N2Controller) and override the OnError method of the Controller. In this method do my exception handling process. Before I show the code I will show you how we can use the ExceptionHandlerAttribute.

The ExceptionHandlerAttribute has three argument, exceptionHandler, viewName, exceptionType. The exceptionHandler argument is where we pass the type of the pre ExceptionHandler we want to use when an Exception is thrown. The viewName is the name of the View to render the exception information (In this sample code the view specified must be located in the Views/Exceptions folder). The exceptionType takes a type of an Exception. If we only want to make sure the Exception Handler should only handle a specific exception thrown within the Controller or Action method, we use the exceptionType argument.

Here is an example of different use of the ExcetpionHandlerAttribute, it can be used on both the Controller’s Class and Action methods:

1. [ExceptionHandler(“Error”);
2. [ExceptionHandler("Error", typeof(DivideByZeroException))]
3. [ExceptionHandler(typeof(ExceptionHandler), "Error")]
4. [ExceptionHandler(typeof(ExceptionHandler), "Error", typeof(DividedByZeroException))
5. [ExceptionHandler(typeof(ExceptionHandler))
6. [ExceptionHandler(typeof(ExceptionHandler), typeof(DividedByZeroException))

1 Will who the Error view when an exception is thrown.
2 Will only show the Error View is the exception thrown is of type DividedByZeroException.
3 Will Show the Error View and use a pre ExceptionHandler before the View is rendered.
4 Will Show the Error View only if the DividedByZeroException is thrown, and use a pre ExceptionHandler before the View is rendered.
5 Will use the ExceptionHandler when an exception is thrown.
6 Will use the ExceptionHandler if the DividedByZeroException is thrown.

To use ExceptionHandler we simply create a Class that implements the IExceptionHandler interface.

public interface IExceptionHandler
{
    void PreExceptionHandling(string actionName, Exception exception);
}

In the PreExceptionHandling method we could for example put logic to log an exception. The PreExceptionHandling method will be called before a View will be rendered (A View will not be rendered if it’s not specified).

Here is an example how the ExceptionHandlerAttribute can be used:

[ExceptionHandler("DefaultError", typeof(DivideByZeroException))]   <- Will not be used
public class HomeController : N2Controller
{
   [ControllerAction, ExceptionHandler("Error")]  <- Will be used
   public void Index()
   {
    
   }
}

The attribute specified on the action method will have more to sat than the attribute on the Controller’s class.

[ExceptionHandler("DefaultError", typeof(DivideByZeroException))]   <- Will be used
public class HomeController : N2Controller
{
    [ControllerAction, ExceptionHandler("Error",typeof(IndexOutOfRangeException),typeof(ArgumentException))]  <- Will be used
    public void Index()
    {
        ...
    }
}

In the code above, both Exception Handler will be used. For example, if the Action method throws a DidivedByZeroException the DefaultError View will be rendered. But if the IndexOurOfRangeException or ArgumetnException is thrown the Error View will be displayed.

Here is the implementation of my custom Controller:

public class N2Controller : Controller
{
    protected override bool OnError(string actionName, System.Reflection.MethodInfo methodInfo, Exception exception)
    {
        ArrayList attributes = this.GetExceptionHandlerAttribute(methodInfo);

        foreach (ExceptionHandlerAttribute attribute in attributes)
        {
           foreach (Type exceptionType in attribute.ExceptionTypes)
           {
              if (exceptionType.IsAssignableFrom(exception.InnerException.GetType()))
              {
                 if (attribute.ExceptionHandler != null)
                    ((IExceptionHandler)Activator.CreateInstance(attribute.ExceptionHandler)).PreExceptionHandling(actionName, exception);

                 if (!string.IsNullOrEmpty(attribute.View))
                     RenderView("~/Views/Exceptions/" + attribute.View + ".aspx", exception);
                    
                 return false;
              }
           }
       }

       return true;
   }

   private ArrayList GetExceptionHandlerAttribute(System.Reflection.MethodInfo methodInfo)
   {
       ArrayList attributes = new ArrayList();

       attributes.AddRange(
                           methodInfo.GetCustomAttributes(
                                 typeof(ExceptionHandlerAttribute),
                                 false));

       attributes.AddRange(
                           this.GetType().GetCustomAttributes(
                           typeof(ExceptionHandlerAttribute),
                           true));
            
       return attributes;
    }
}

The code above is only on a prototype stage and. If no ExceptionHandlerAttribute is specified for an Action or Controller, the original ASP.Net MVC Frameowork’s OnError behavior will be used.

4 Comments

  • Good stuff. However I hope this is going to be a part of the ASP.NET MVC framework and not an extension.

  • Nice solution, but I think it would be better to redirect to action than render view. Your proposal depends on existing view in Exceptions folder. Using RedirectToAction we can redirect to any controller/action. As a simple example of this could be redirecting to Login controller when the SecurityException is thrown...

  • Great solution!

    Could you share your implementation of your ExceptionHandlerAttribute class as well?

    Cheers!

  • Hi great idea.
    Could you please share your implementation of the ExceptionHandlerAttribute code.

    Thanks!

Comments have been disabled for this content.