October 25, 2013

6 Ways To Handle The Exceptions In Asp.Net MVC 4

Handling exceptions and presenting them to the user in appropriate way instead of default error message is best approach to tell end user "What to do when it happen?"

I found several ways to handle errors but I'm posting the popular ones.

1. Redirect to Custom Error - Simplest Approach

In try..catch block, if you want to redirect to custom error page on the basis of specific conditions you can use HandleErrorInfo class to do so. Example is

// Define variable of type Exception & HandleErrorInfo Exception exception; HandleErrorInfo errorInfo; // Do something like you filter the Client with Id // And clientPresent is a variable which hold Client data which // is use futher to insert its Customers if (clientPresent != null) { // Add its Customer } else { exception = new Exception("Unable to recognize the Client!"); errorInfo = new HandleErrorInfo(exception, "Home", "Index"); return View("Error", errorInfo); }

In above code, you define the appropriate message, add it to HandleErrorInfo class. HandleErrorInfo class contains three parameters.
  • Exception           : Custom format of message
  • Controller Name  : Where this exception raise
  • Action Name       : Action name contains exception

2. Writing try..catch

A classic approach to catch any exception. You use this inside the controller code and handle the exceptions that may occur. Here the only thing you have to take into account is what is the controller action is returning. Here you have two approaches & choose one to follow. First approach is "If you return a view, when you catch an exception you should redirect to an error page." is define above and second approach is "when you return JSON you should return a JSON with the exception message and handle it on the client side" is define here:

public ActionResult Index() { try { //Do stuff here } catch (Exception e) { //Redirect to an error page Response.Redirect("URL to the errror page"); } return View(); } public JsonResult GetJson() { try { } catch (Exception e) { //Return the exception message as JSON return Json(new { error = e.Message }); } return Json("Return data here"); }

The main disadvantage of this approach is that you have to use it everywhere and you bloat the code.

3. Override OnException method inside the Controller

You can handle all the exceptions inside a controller is by overriding the OnExeception method. With this approach you code only one exception handling routine per

Controller and you don`t bloat the code. Example is:

protected override void OnException(ExceptionContext filterContext) { //If the exeption is already handled we do nothing if (filterContext.ExceptionHandled) { return; } else { //Determine the return type of the action string actionName = filterContext.RouteData.Values["action"].ToString(); Type controllerType = filterContext.Controller.GetType(); var method = controllerType.GetMethod(actionName); var returnType = method.ReturnType; //If the action that generated the exception returns JSON if (returnType.Equals(typeof(JsonResult))) { filterContext.Result = new JsonResult() { Data = "Return data here" }; } //If the action that generated the exception returns a view if (returnType.Equals(typeof(ActionResult)) || (returnType).IsSubclassOf(typeof(ActionResult))) { filterContext.Result = new ViewResult() { ViewName = "URL to the errror page" }; } } //Make sure that we mark the exception as handled filterContext.ExceptionHandled = true; }

But same disadvantage that you have to override the OnExecption method inside every controller it`s not the best approach. You need to react in a different way if your exception throwing action returns a view or returns JSON. Also, you have to make sure that you specify that the exception was handled. Skipping this will throw the exception further.

4. Using the HandleError attribute

You can use HandleError attribute. When you provide this attribute to your controller class or to your action method, when an unhandled exception is encountered MVC will look first for a corresponding view named Error in the controller`s view folder. If it can`t find it there, it will look inside the shared view folder and redirect to that view.

[HandleError] public ActionResult Index() { return View(); }

You can also filter the type of error you want to handle

[HandleError(ExceptionType = typeof(SqlException))] public ActionResult Index() { return View(); }

5. Extending the HandleError attribute

You can code your own logic to handle errors using attributes. All you have to do is to extend the existing HandleErrorAttribute and override the OnException method. All you need to create a custom HandleErrorAttribute using the logic mentioned above.

public class HandleCustomError : System.Web.Mvc.HandleErrorAttribute { public override void OnException(System.Web.Mvc.ExceptionContext filterContext) { //If the exeption is already handled we do nothing if (filterContext.ExceptionHandled) { return; } else { //Determine the return type of the action string actionName = filterContext.RouteData.Values["action"].ToString(); Type controllerType = filterContext.Controller.GetType() ; var method = controllerType.GetMethod(actionName); var returnType = method.ReturnType; //If the action that generated the exception returns JSON if (returnType.Equals(typeof(JsonResult))) { filterContext.Result = new JsonResult() { Data = "Return data here" }; } //If the action that generated the exception returns a view //Thank you Sumesh for the comment if (returnType.Equals(typeof(ActionResult)) || (returnType).IsSubclassOf(typeof(ActionResult))) { filterContext.Result = new ViewResult { ViewName = "URL to the errror page" }; } } //Make sure that we mark the exception as handled filterContext.ExceptionHandled = true; } }

You use this custom implementation like this

[HandleCustomError] public ActionResult Index() { return View(); }

6. Handle all errors inside Global.asax

This method uses a global error handling that captures all exceptions into a single point. The only problem is to determine if we have to return a view or some JSON in case of AJAX request. Included in the snippet below there is a method that uses reflection to do all this.

public class MvcApplication : System.Web.HttpApplication { void Application_Error(object sender, EventArgs e) { //We clear the response Response.Clear(); //We check if we have an AJAX request and return JSON in this case if (IsAjaxRequest()) { Response.Write("Your JSON here"); } else { //We don`t have an AJAX request, redirect to an error page Response.Redirect("Your error page URL here"); } //We clear the error Server.ClearError(); } //This method checks if we have an AJAX request or not private bool IsAjaxRequest() { //The easy way bool isAjaxRequest = (Request["X-Requested-With"] == "XMLHttpRequest") || ((Request.Headers != null) && (Request.Headers["X-Requested-With"] == "XMLHttpRequest")); //If we are not sure that we have an AJAX request or that we have to return JSON //we fall back to Reflection if (!isAjaxRequest) { try { //The controller and action string controllerName = Request.RequestContext. RouteData.Values["controller"].ToString(); string actionName = Request.RequestContext. RouteData.Values["action"].ToString(); //We create a controller instance DefaultControllerFactory controllerFactory = new DefaultControllerFactory(); Controller controller = controllerFactory.CreateController( Request.RequestContext, controllerName) as Controller; //We get the controller actions ReflectedControllerDescriptor controllerDescriptor = new ReflectedControllerDescriptor(controller.GetType()); ActionDescriptor[] controllerActions = controllerDescriptor.GetCanonicalActions(); //We search for our action foreach (ReflectedActionDescriptor actionDescriptor in controllerActions) { if (actionDescriptor.ActionName.ToUpper().Equals(actionName.ToUpper())) { //If the action returns JsonResult then we have an AJAX request if (actionDescriptor.MethodInfo.ReturnType .Equals(typeof(JsonResult))) return true; } } } catch { } } return isAjaxRequest; } //snip }

Hope you understand every approach clearly. You can use any of them which well suite you.

2 comments: