Friday, December 14, 2018

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-finallyChange 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 methodFor 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 AttributeThis 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 in web.config under

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 HandleErrorAttributeWe 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. }  

All MVC exception handling techniques discussed till now do not handle HTTP errors like file not found, HTTP 500 error’s etc. For that we need to make an entry of the error action and the error status code as shown in the below config file.
<system.web>
<customErrors

                  mode="On" defaultRedirect="Error1">
<error statusCode="404" redirect="~/Testing/NoPageFound"/>
</customErrors>
</system.web> 

No comments:

Followers

Link