4 Ways to do MVC Error Handling
Between .NET, ASP.NET, and MVC there are several potential
ways to handle application
errors.
- Web.Config
customErrors
- MVC HandleErrorAttribute
- Controller.OnException
method
- HttpApplication
Application_Error event
Must Have: Global
Error Page With Web.Config <customErrors>
The last thing you ever want your users to see is a “yellow
screen of death” type error. If you don’t know what that is, I’m referring the
standard yellow ASP.NET error screen.
For any application, I would always recommend specifying a
custom error page in your Web.Config. Worst case scenario, your users will see
this page if an unhandled exception occurs.
<system.web>
<customErrors
mode="On" defaultRedirect="~/ErrorHandler/Index">
<error
statusCode="404" redirect="~/ErrorHandler/NotFound"/>
</customErrors>
<system.web/>
Use MVC HandlerErrorAttribute to
Customize Responses
The HandleErrorAttribute inherits from FilterAttribute
and can be applied to an entire controller or individual controller action
methods.
It can only handle 500 level errors that happen within an
MVC action method. It does not track exceptions that help outside of the MVC
pipeline. Exceptions may occur in other HTTP modules, MVC
routing, etc.
When to Use HandleErrorAttribute
Since it does not provide a way to collect all exceptions
that could ever happen, it is a bad solution for a global unhandled error
handler.
It works perfectly for tailoring specific error pages
for a specific MVC controller or action method. Specifying an error page in
your Web.Config <customErrors> works ideal for a universal error page.
The HandleErrorAttribute gives you fine-grained control if you need it.
Note: HandleErrorAttribute requires customErrors to be
enabled in your Web.Config.
For example, if you wanted to show a particular MVC view
when a SqlException happens, you can do it with the code below:
[HandleError(ExceptionType = typeof(SqlException), View =
"SqlExceptionView")]
public string GetClientInfo(string username)
{
return
"true";
}
The problem with HandleErrorAttribute is it doesn’t provide a way to
log the exception!
Use MVC Controller OnException to
Customize Responses
OnException is similar to HandleErrorAttribute but provides
more flexibility. It works with all HTTP status codes, and not just 500
level responses. It also gives you the ability to log the errors!
public class UserMvcController : Controller
{
protected override void
OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled
= true;
//Log the error!!
_Logger.Error(filterContext.Exception);
//Redirect or
return a view, but not both.
filterContext.Result
= RedirectToAction("Index", "ErrorHandler");
// OR
filterContext.Result
= new ViewResult
{
ViewName = "~/Views/ErrorHandler/Index.cshtml"
};
}
}
When to Use OnException for MVC Error
Handling
If you want a way to present your users custom MVC views or
custom log exceptions, OnException is a good solution for you. It provides more
flexibility than HandleErrorAttribute and does not require customErrors to be
enabled in your Web.Config file.
Note: OnException gets called for all HTTP status codes. So
be careful how you handle simple issues like a 404 caused by a bad URL.
public class BaseController : Controller
{
protected override void
OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
ViewData["Data"] =
filterContext.Exception.Message.ToString();
filterContext.Result = new ViewResult { ViewData = this.ViewData, ViewName = "Error" };
base.OnException(filterContext);
}
}
Use HttpApplication Application_Error
as Global Exception Handler
So far we have covered three different ways to customize the
response that your users see if an exception occurs. Only within OnException
can you potentially log exceptions.
To log all unhandled exceptions that may occur within your
application, you should implement basic error logging code as shown below.
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
protected void Application_Error()
{
var ex = Server.GetLastError();
//log the error!
_Logger.Error(ex);
}
}
When to Use
Application_Error
Always! HttpApplication’s Error even provides the best
mechanism to collect and log all unhandled application errors.