Sunday, March 29, 2015

Understanding the HandleError attribute in ASP.NET MVC

If you are new to ASP.NET MVC, you may not be aware of the HandleError attribute available for handling errors within an ASP.NET MVC Web Application.

Basically, you can add the following line of code in the FilterConfig.cs file and ASP.NET MVC will supposedly handle all errors that occurs within the MVC Web Application:

filters.Add(new HandleErrorAttribute());



However, one caveat to being able to use the HandleError attribute is that it will ONLY handle errors or exceptions that occur within the ASP.NET MVC Web Application such as 500 (Internal Server) errors.  Any other exceptions that occur before the ASP.NET MVC Execution Pipeline will not be handled (such as as loading exceptions or 404-Page Not Found Error messages etc.)

In addition, if you do not set the customErrors element to "On" in Web.config, it will also not trigger the handling of those errors:




<customErrors mode="On" defaultRedirect="~/Views/Shared/Error" />

If you simply want to redirect to the Error View without any additional processing in a specific Controller, you can use the element as outlined above.


Then, you can define a basic Razor Error View such as the following:




@model System.Web.Mvc.HandleErrorInfo

 

<!DOCTYPE html>

<html>

<head>

    <meta name="viewport" content="width=device-width" />

    <title>Error</title>

</head>

<body>

    <hgroup>

        <h1>Error.</h1>

        <h2>

            An error occurred while processing your request.

            <br />

            Error Message: @Model.Exception

            <br />

            Action Method: @Model.ActionName

            <br />

            Controller Name: @Model.ControllerName

        </h2>

    </hgroup>

</body>

</html>


However, what if you want to handle other exceptions or errors that occur in your ASP.NET MVC Web Application?


Well, you still have to resort to handling those errors in your Global.asax events—namely the Application_Error event handler.


This article does a very good job of covering the scenarios to handle other errors outside of the ASP.NET MVC Execution pipeline: http://www.codeproject.com/Articles/422572/Exception-Handling-in-ASP-NET-MVC

These articles also do a good job of discussing the same approach to MVC Error Handling: http://michael-mckenna.com/blog/error-handling-in-asp-dot-net-mvc-part-1-our-options
http://michael-mckenna.com/blog/error-handling-in-asp-dot-net-mvc-part-2-our-implementation

When it comes to the Global.asax, it provides the following code:



protected void Application_Error(object sender, EventArgs e)

{

   var httpContext = ((MvcApplication)sender).Context;

   var currentController = " ";

   var currentAction = " ";

   var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));

 

   if (currentRouteData != null)

   {

       if (currentRouteData.Values["controller"] != null && !String.IsNullOrEmpty(currentRouteData.Values["controller"].ToString()))

       {

           currentController = currentRouteData.Values["controller"].ToString();

       }

 

       if (currentRouteData.Values["action"] != null && !String.IsNullOrEmpty(currentRouteData.Values["action"].ToString()))

       {

           currentAction = currentRouteData.Values["action"].ToString();

       }

   }

 

   var ex = Server.GetLastError();

   var controller = new ErrorController();

   var routeData = new RouteData();

   var action = "ErrorHandler";

 

   if (ex is HttpException)

   {

       var httpEx = ex as HttpException;

 

       switch (httpEx.GetHttpCode())

       {

           case 404:

               action = "NotFound";

               break;

 

           // others if any

       }

   }

 

   httpContext.ClearError();

   httpContext.Response.Clear();

   httpContext.Response.StatusCode = ex is HttpException ? ((HttpException)ex).GetHttpCode() : 500;

   httpContext.Response.TrySkipIisCustomErrors = true;

 

   routeData.Values["controller"] = "Error";

   routeData.Values["action"] = action;

 

   controller.ViewData.Model = new HandleErrorInfo(ex, currentController, currentAction);

   ((IController)controller).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData));

}


1 comment: