Generic Exception Handling Techniques In ASP.NET MVC

Error handling is the most common and important part of any web, desktop or any other software application, no matter how proficient the developer is who developed it. Your application throws an exception when some part of the code is not executing as you expected or the users may enter some wrong input or network related issue. Exceptions(errors) are like a bacteria and a developer can not run away from them. So what to do? Simple: Handle every kind of exception keeping the user experience in mind. So let’s come up with how to handle exceptions in a good and not so good way.

1. try ....catch:

It is a very simple and easy way, just put try catch block where you want to handle your exception into your action method ,any class method and properties etc. In Catch block , you can do whatever you want with exceptions, you can log it, email to admin, or just swallow it. Below code illustrates it,
  1. public ActionResult Index()  
  2.         {  
  3.             try  
  4.             {  
  5.                 // ... Any logic           
  6.                 return View();  
  7.             }  
  8.             catch (Exception ex)  
  9.             {  
  10.                 // here you can do everything with ex what ever you want (and if you are lazy like me, continue reading to next topic)         
  11.                 return View("Error");  
  12.             }  
  13.         }  

2. Override OnException method:

Inthe previous section I discussed about the  try ....catch technique for code level exception handling.It means that you need to find out all places into your project that an  exception can be thrown. This is not a good way. It is very hard work to write try catch everywhere that code can throw exceptiosn. So now let's try to make some reusable code. Now we will try to use base class to make reusable code on controller level.
  1. protected override void OnException(ExceptionContext filterContext)  
  2. {     
  3.  Var lastException = filterContext.Exception;      
  4. // Do something with exception, e.g. Log it or  mail to Admin.      
  5. filterContext.ExceptionHandled = true;      // So that no one else will be bothered      
  6. filterContext.Result = new ViewResult() { ViewName = "Error" };  
  7. }  
 Now, we can handle all exceptions in controller and we can also send custom error view to browser if any exception occurred. But this is having some limitation e.g.
  1. It handles only controller level exception.
  2. If any exception occurred then it response HTTP 200 when it is rendering error view, and also search engine can also crawl this, hence it is also not a good idea.
So what to do, let's think about some more generic way to handle exception
e.g. Global Exception handling.
 
3. Exception Handling using HandleErrorAttribute:

To achieve global level exception handling , now we need to do some modification into our we.Config file. In System.Web.Section of web.config , we need to add one more section customErrors Section and then add the name of controller (which have error Action)with returning error view or you can also give any HTML page for error. Here we are giving instruction to asp.net that capture all unhandled exceptions and redirect to our custom error page, which we already set into we.config. Here we can also capture individual http error and redirect to any specific custom error page. But still this is not a good solution because using this we can not do some custom processing . So now lets Explore the feature of MVC , known as HandleErrorAttribute action filter.
 
HandleErrorAttribute will work when custom error mode on in web.config file. When you are using this then make sure that you on custom error. In web.config. E.g.  
  1.  <system.web>    
  2.    <customErrors mode="On"></customErrors>    
  3. </system.web>   
By using HandleErrorAttribute we can handle error at Action level, Controller level and Global level. Lets discuss basic properties of HandleErrorAttribute:
 
Properties of HandleErrorAttribute:
  • ExceptionType: By using we can handle specific exception.If we do not specified it then it will handle all the exceptions.
  • View: It mean Name of view which will display the information of exception when exception occurred.
  • Master: This property is use to set Master view of displaying exception message.
  • Order: It means execute filters by priority . It have integer value and 1 indicate highest priority.
  • AllowMultiple: This is used when we want to create multiple object of error filter. 
There are some of the way to handle execption using  HandleErrorAttribute.
 
a. Action Level Exception Handling using HandleError:

If you want to handle exceptions on Action level using HandleError attribute simply Add HandleError attribute on you action method with its optional properties. E.g. 
 
  1. [HandleError(ExceptionType = typeof(DivideByZeroException), View = "dividbyzeroErrorView")]    
  2.      public ActionResult Index(int id)    
  3.      {    
  4.          var result = id / 0;    
  5.          return View("Index", result);    
  6.      }     
In the above example, at the run time when database related exception means System.Data.DataException has occurred then MVC will redirect dbErrorView.If you want to handle all exception then use only HandleError without its property. 
  1. [HandleError]  
  2.       public ActionResult Index(int id)  
  3.       {  
  4.           var result = 1d / 0;  
  5.           return View("Index", result);  
  6.       }  
In Above example the HandleError will handle all types of exceptions which will occurr during index method execute and customerrors mode on with defaultRedirect into our web.config file e.g.
  1.   <system.web>  
  2.     <customErrors mode="On"  defaultRedirect="error.html"></customErrors>  
  3. </system.web>  
b. Controller Level Exception Handling: 

As with Action level exception we can also handle exceptions at controller level which means if error occurred in any of the action methods of this controller then HandleError filter will call and Handle error. Handling controller level exception is pretty easy just decorate you controller with HandlerError Attribute according to your requirement. e.g. 
  1. [HandleError(ExceptionType = typeof(DivideByZeroException), View = "DividByZeroError")]  
  2. [HandleError(ExceptionType=typeof(NullReferenceException),View= "CustomErrorPage")]  
  3. public class HomeController : Controller  
  4. {  
  5.     // Here HaldleError Handle exception into all your action method of this controller..     
  6. }  
It has a limitation that we need to decorate each controller of our application where we want to handle exception using HandleError Attribute.
 
c. Global Level Exception Handling: 

In previous points we handle exception on Controller level. But if I want to handle exception globally then what to do , Its simple We need to add HandleError attribute in FilterConfig.cs . We can find FilterConfig.cs in App-Start folder of our MVC Application.
  1. public class FilterConfig  
  2. {  
  3.    public static void RegisterGlobalFilters(GlobalFilterCollection filters)  
  4.    {  
  5.      filters.Add(new HandleErrorAttribute());  
  6.    }
  7. }  
 In above code FilterConfig class contains one static method name RegisterGlobalFilters which is have one argument GlobalFilterCollection as input and now we added HandleError Attribut in it.in Global.asax file invoked this method . e.g. 
  1. protected void Application_Start()  
  2. {  
  3.       AreaRegistration.RegisterAllAreas();  
  4.       //Invoked  RegisterGlobalFilters             
  5.       FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);  
  6.       RouteConfig.RegisterRoutes(RouteTable.Routes);  
  7.       BundleConfig.RegisterBundles(BundleTable.Bundles);
  8. }   
Now it will handle exception of all controller but this is still have limitations.
  • We can not exception log or email to Admin.
  • When error occurred out side the controller then It will not catch exception.
  • At Ajax request time , If any exception occurred then its will return error view , which is not a good things.
So finally the solution I like out of all this problems are extending HandleError Attribute.
 
d. Extending HandleErrorAttribute: 

Let's Create a generic error Handling attribute which handle all exception as well log error into database or mail to admin according to your requirement or return JSON error object for Ajax call if any exception occurred. So lets dive into code without boring you much. 
  1. //CustomErrorAttribute  is inherit from HandleErrorAttribute  
  2. public class CustomErrorAttribute : HandleErrorAttribute  
  3. {  
  4.     //Now Override OnExption   
  5.     public override void OnException(ExceptionContext filterContext)  
  6.     {  
  7.         //First Check if Exception all ready Handle Or Check Is Custom error Handle is enable   
  8.         if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)  
  9.         {  
  10.             return;  
  11.         }  
  12.         var statusCode = (int)HttpStatusCode.InternalServerError;  
  13.   
  14.         if (filterContext.Exception is HttpException)  
  15.         {  
  16.             statusCode = new HttpException(null, filterContext.Exception).GetHttpCode();  
  17.         }  
  18.   
  19.   
  20.         // if the request is AJAX return JSON else view.  
  21.         if (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")  
  22.         {  
  23.             filterContext.Result = new JsonResult  
  24.             {  
  25.                 JsonRequestBehavior = JsonRequestBehavior.AllowGet,  
  26.                 Data = new  
  27.                 {  
  28.                     error = true,  
  29.                     message = filterContext.Exception.Message  
  30.                 }  
  31.             };  
  32.         }  
  33.         else  
  34.         {  
  35.             var controllerName = filterContext.RouteData.Values["controller"].ToString();  
  36.             var actionName = filterContext.RouteData.Values["action"].ToString();  
  37.             var errormodel = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);  
  38.   
  39.             filterContext.Result = new ViewResult  
  40.             {  
  41.                 ViewName = View,  
  42.                 MasterName = Master,  
  43.                 ViewData = new ViewDataDictionary(errormodel),  
  44.                 TempData = filterContext.Controller.TempData  
  45.             };  
  46.         }  
  47.   
  48.         // log the error by using your own method  
  49.         //here you can log  error into db or mail error with details to Admin or any other .  
  50.         //Suppose I created one method name ExeptionLog() which accept one input parament type  exception. So now can paly with this exception into my exception log method.  
  51.         
  52.         //ExeptionLog(filterContext.Exception);  
  53.   
  54.         // Prepare the response code.  
  55.         filterContext.ExceptionHandled = true;  
  56.         filterContext.HttpContext.Response.Clear();  
  57.         filterContext.HttpContext.Response.StatusCode = statusCode;  
  58.   
  59.         filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;  
  60.     }  
  61. }   
Conclusion: 

Exception handling is an important part of any Software application. So now by extending HandleErrorAttribute we can play with exceptions in asp.net MVC. Your comments, feedback, suggestions and critique are welcomed.
 
Read more articles on ASP.NET:


Similar Articles