/backend /frontend /tools

Error handling is a widespread task in the most applications. This aspect is incredibly important while user after seeing popup message with Critical error and a bunch of strange symbols will close this application and will avoid to open it ever again. And not less attention should be paid to architectural decision that will allow to use global error handler instead of using copied code overall.

By default in MVC applications (v.2+) you have possibility to switch on standard error handler. To do this you should simply add next line to config's section :

Let's add action to home controller to check this:

public ActionResultHelloError() { throw new Exception("Here is exception occured"); } 1 2 3 public ActionResultHelloError ( ) { throw new Exception ( "Here is exception occured" ) ; }

When you call this action you will get something like this:

After that user gets heart attack and switches to mode “it wasn’t me”. Now we switch flag to “on” and try once more.

This time our exception was handled and user got some clear (or almost clear) error-message.

How does it work? Secret is in default configuration of MVC-application.



On start application calls method RegisterGlobalFilters() which performs following action:

public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(newHandleErrorAttribute()); } 1 2 3 public static void RegisterGlobalFilters ( GlobalFilterCollection filters ) { filters . Add ( newHandleErrorAttribute ( ) ) ; }

This is the one that handles exceptions in MVC applications' actions. By default all errors are rendered to “Error” view (that can be found in ~/Views/Shared). Behavior of HandleErrorAttribute can be configured through following attributes:

ExceptionType. Specifies the exception type or types that the filter will handle. If this property is not specified, the filter handles all exceptions.

View. Specifies the name of the view to display.

Master. Specifies the name of the master view to use, if any.

Order. Specifies the order in which the filters are applied, if more than oneHandleErrorAttribute filter is possible for a method.

MVC framework also adds information about exceptions occurred in ViewDataDictionary instance, where Model will contain exemplar of ExceptionContext. The following information is stored In dictionary ViewData:

ActionName. The intended action method.

ControllerName. The intended controller.

Exception. The exception object.

Method described before is very easy to implement, but HandleErrorAttribute catches only unhandled controllers' exception. If exception was raised in some other place then user will get that scary error message shown before or error page, set up in customErrors in Web.config. Standard mechanism works fine for restricted class of tasks, but sometimes full control for exception handling is needed.

ASP.NET provides few levels for error handling in the application. There are 3 methods to do this: Page_Error, Application_Error and configuration file (Web.config mentioned before).

Let’s use method Application_Error in Global.asax and redirect application to expected actions. First of all we create ErrorController, that will perform redirection, and few actions inside. Below you can see complete implementation:

public class ErrorController: Controller { public ViewResultError(Exception exception) {@ ViewData["criticalMessage"] = "An unhandled exception occured: " + exception.Message; return View("Error", exception); } public ViewResultError404(Exception exception) {@ ViewData["criticalMessage"] = "The resource you are looking for is not found."; return View("Error", exception); } public ViewResultError500(Exception exception) {@ ViewData["criticalMessage"] = "Internal server error: " + exception.Message; return View("Error", exception); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class ErrorController : Controller { public ViewResultError ( Exception exception ) { @ ViewData [ "criticalMessage" ] = "An unhandled exception occured: " + exception . Message ; return View ( "Error" , exception ) ; } public ViewResultError404 ( Exception exception ) { @ ViewData [ "criticalMessage" ] = "The resource you are looking for is not found." ; return View ( "Error" , exception ) ; } public ViewResultError500 ( Exception exception ) { @ ViewData [ "criticalMessage" ] = "Internal server error: " + exception . Message ; return View ( "Error" , exception ) ; } }

Now we will write extension method for Exception class that will try to get ErrorCode and redirect call accordingly to ErrorController

public static void Render(thisException exception, HttpContext context) { var routeData = newRouteData(); var action = "Error"; var httpException = exception asHttpException; if (httpException != null) { switch (httpException.GetHttpCode()) { case 404: action = "Error404"; break; case 500: action = "Error500"; break; default: action = "Error"; break; } } routeData.Values.Add("controller", "Error"); routeData.Values.Add("action", action); routeData.Values.Add("exception", exception); IController errorController = newErrorController(); errorController.Execute(newRequestContext(newHttpContextWrapper(context), routeData)); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public static void Render ( thisException exception , HttpContext context ) { var routeData = newRouteData ( ) ; var action = "Error" ; var httpException = exception asHttpException ; if ( httpException ! = null ) { switch ( httpException . GetHttpCode ( ) ) { case 404 : action = "Error404" ; break ; case 500 : action = "Error500" ; break ; default : action = "Error" ; break ; } } routeData . Values . Add ( "controller" , "Error" ) ; routeData . Values . Add ( "action" , action ) ; routeData . Values . Add ( "exception" , exception ) ; IController errorController = newErrorController ( ) ; errorController . Execute ( newRequestContext ( newHttpContextWrapper ( context ) , routeData ) ) ; }

So, in this method we’ll try to cast exception to HttpException after that we’ll read error code and define action for redirection. And for catching excptions we’ll use Application_Error.

protected void Application_Error(object sender, EventArgs e) { var ctx = HttpContext.Current; ctx.Server.GetLastError().Render(Context); ctx.Server.ClearError(); } 1 2 3 4 5 protected void Application_Error ( object sender , EventArgs e ) { var ctx = HttpContext . Current ; ctx . Server . GetLastError ( ) . Render ( Context ) ; ctx . Server . ClearError ( ) ; }

Don’t forget to set flag in web.config to default value.

<customErrorsmode="Off">

That’s all. When we try to raise 404 error our application will redirect us to the page needed

1.http://msdn.microsoft.com/en-us/library/system.web.mvc.handleerrorattribute(v=vs.118).aspx

2.http://support.microsoft.com/kb/306355/ru

3.http://www.c-sharpcorner.com/UploadFile/abhikumarvatsa/handleerrorattribute-or-error-handling-in-mvc-4/

4.https://gist.github.com/confa/3339210