Exception Handling in MVC

Exception handling is the process of responding to the occurrence of exceptional conditions requiring special processing. Exception handling is important in any application. In ASP.NET we can handle exceptions in the following two ways:
  • Try-catch-finally block at method level
  • Using Application_Error

Exception Handling in MVC

 
In ASP.NET MVC we have a larger list of ways to handle exception such as:
  • Try-catch-finally
  • Overriding OnException method
  • Using the [HandleError] attribute on actions and controllers
  • Setting a global exception handling filter
  • Handling Application_Error event
  • Extending HandleErrorAttribute
To begin with, create a new empty ASP.NET MVC application. Add a controller as "ExceptionTestingController". Add a view for Index. Add another view at "Views\Shared" as "Error.cshtml".
 
For testing purpose we would cause some kind of exception.
 

try-catch-finally

 
Change the Index as in the following.
  1. public ActionResult Index()  
  2. {  
  3.     int a = 1;  
  4.     int b = 0;  
  5.     int c = 0;  
  6.     try  
  7.     {  
  8.         c = a / b; //it would cause exception.  
  9.     }  
  10.     catch (Exception ex)  
  11.     {  
  12.         return View("Error");  
  13.     }  
  14.     finally  
  15.     {   
  16.     }  
  17.     return View();  
  18. }  
Here we are generating an exception at "c = a / b;" inside the try block. In the catch we are retuning a different view "Error". If you run the application you will get an Error page in the browser. In an actual project we can also use the catch block to log the error information.
 

Overriding OnException method

 
For this, remove the try block from the preceding code. We need to overwrite OnException as in the following:
  1. public ActionResult Index()  
  2. {  
  3.     int a = 1;  
  4.     int b = 0;  
  5.     int c = 0;  
  6.     c = a / b; //it would cause exception.             
  7.     return View();  
  8. }  
  9.   
  10. protected override void OnException(ExceptionContext filterContext)  
  11. {  
  12.     string action = filterContext.RouteData.Values["action"].ToString();   
  13.     Exception e = filterContext.Exception;  
  14.     filterContext.ExceptionHandled = true;  
  15.     filterContext.Result = new ViewResult()  
  16.     {  
  17.         ViewName = "Error"  
  18.     };  
  19. }  
OnException is a void method that takes an argument as an object of ExceptionContext that has all the information about the exception that can be used to log. We need to set ExceptionHandled = true for the ExceptionContext object. We can handle the exception generated from all the actions from a specific controller. We can get the action name from ExceptionContext as in:
  1. string action = filterContext.RouteData.Values["action"].ToString();   
There is the problem with this approach that we cannot reuse the exception handling logic across multiple controllers. That is where global error handling is useful.
 

Using HandleError Attribute

 
This is a simple way to handle exceptions in MVC. Remove the OnException implementation from the previous code. Use the following procedure to do that.
 
Step 1
 
Add <customErrors mode="On" ></customErrors> in web.config under <system.web>.
 
Step 2
 
Decorate the action with [HandleError] as:
  1. [HandleError]  
  2. public ActionResult Index()  
  3. {  
  4.    int a = 1;  
  5.    int b = 0;  
  6.    int c = 0;  
  7.    c = a / b; //it would cause exception.   
  8.    return View();  
  9. }  
We can handle a different exception with a different view with [HandleError] as in the following:
  1. [HandleError]  
  2. [HandleError(ExceptionType = typeof(DivideByZeroException), View = "Error1")]  
  3. [HandleError(ExceptionType = typeof(ArgumentOutOfRangeException), View = "Error2")]  
  4. public ActionResult Index()  
  5.  {  
  6.      int a = 1;  
  7.      int b = 0;  
  8.      int c = 0;  
  9.      c = a / b; //it would cause exception.             
  10.      return View();  
  11.  }  
Here we should place a specific HandleError at the bottom. If we reverse the order of the HandleError attribute then the Error.cshtml will always be displayed.
 
In the same way we can decorate our controller with HandleError. This would handle the exception generated from all the actions from a specific controller.
 
Limitations:
  • Does not support logging exceptions.
  • Doesn't catch HTTP exceptions other than 500.
  • Doesn't catch exceptions that are raised outside controllers.
  • Returns an error view even for exceptions raised in AJAX calls.

Setting a global exception handling filter

 
For this we need to ensure that HandleErrorAttribute is added in RegisterGlobalFilters of the FilterConfig file of App_start and registered in Application_Start. No need to decorate our action and controller with [HandleError].
 

Extending HandleErrorAttribute

 
We can also create our own Exception Handler by inheriting from HandleErrorAttribute as in the following:
  1. public class MyExceptionHandler : HandleErrorAttribute  
  2. {  
  3.      public override void OnException(ExceptionContext filterContext)  
  4.      {  
  5.           if (filterContext.ExceptionHandled ||filterContext.HttpContext.IsCustomErrorEnabled)  
  6.            {  
  7.                return;  
  8.            }  
  9.            Exception e = filterContext.Exception;  
  10.            filterContext.ExceptionHandled = true;  
  11.            filterContext.Result = new ViewResult()  
  12.            {  
  13.                ViewName = "Error2"  
  14.            };  
  15.     }  
  16. }  
We need to set ExceptionHandled = true for the ExceptionContext object. We can handle the exception generated from all the actions from a specific controller. We can also get the action name form ExceptionContext as in the following:
  1. string action = filterContext.RouteData.Values["action"].ToString();   
Now we can decorate our action/controller with [MyExceptionHandler] to use this.
  1. [MyExceptionHandler]  
  2. public ActionResult Index()  
  3. {  
  4.     int a = 1;  
  5.     int b = 0;  
  6.     int c = 0;  
  7.     c = a / b; //it would cause exception.             
  8.     return View();  
  9. }