Handling Errors in ASP.NET MVC Applications

Introduction

No matter how proficiently you developed your application there are chances that your code may not work as expected and will generate an error at runtime. Users may enter some invalid data, mathematical calculations can go wrong, some network level fault may cause errors and more. That is why it is always a good idea to implement a robust error handling mechanism in your web application. To that end ASP.NET MVC offers several techniques that help you build such an error handling mechanism. This article discusses them with examples.

Exception Handling Techniques for ASP.NET MVC

Before we get into the actual error handling techniques offered by ASP.NET MVC, let’s quickly enumerate them here:

  • try…catch
  • Overriding OnException method
  • Using the [HandleError] attribute on actions and controllers
  • Setting a global exception handling filter
  • Handling Application_Error event

The first technique listed above is not specific to ASP.NET MVC; it is applicable to any piece of C# code. However, we will still glance over it for the sake of understanding. If you have ever developed ASP.NET Web Forms applications, you might be aware of the Page_Error event available at the page level. Since ASP.NET MVC doesn’t follow the page life cycle events as such, obviously this event is not available to your application. Something analogous is, however, available through the OnException() method. More on that later. The [HandleError] attribute is possibly the most simple way to deal with errors in an ASP.NET MVC application. This technique doesn’t involve any special controller code other than this attribute. All you need is a custom error page in the form of a View. The last couple of techniques are global level techniques that are applicable to the whole ASP.NET MVC application and not to a particular action or controller. 

Now that you know the error handling techniques available to your application, let’s discuss each of them with a code sample.

To begin with, create a new ASP.NET MVC application. Add an ADO.NET Entity Data Model for the Customers table of Northwind database to the Models folder. The following figure shows the Customer entity:

The Customer Entity
The Customer Entity

Then add the Home controller in the Controllers folder.

Using the Try…Catch Statement

To illustrate the try..catch technique, you will deliberately cause some database related exception. Add the following code in the Index() action method of the HomeController class.

public ActionResult Index()
{
  try
  {
    NorthwindEntities db = new NorthwindEntities();
    Customer obj = new Customer();
    obj.CustomerID = "ABCDEFGHIJK";
    obj.CompanyName = "Company Name 1";
    obj.ContactName = "Contact Name 1";
    obj.Country = "USA";
    db.Customers.Add(obj);
    db.SaveChanges();
  }
  catch(Exception ex)
  {
    return View("Error");
  }
  return View();
}

The above piece of code attempts to insert a Customer whose CustomerID is more than five characters long – a limit defined for the column at the database level. Obviously, at SaveChanges() an exception is thrown. The exception is handled by the catch block. The above code doesn’t handle different exceptions using different catch blocks (which you are likely to do in a real world application), rather it just handles all the possible exceptions using the generic catch block. The catch block simply returns the Error view to the browser. The Error view is intended to display a generic friendly error message to the end user. To add the Error view, create a subfolder named Shared under the Views folder and then add a View (Error.cshtml) inside the Shared folder. This way you can use the same error view for all the controllers of the application. Of course, you could have also placed it in individual view folders if you wanted. The Error view in this case contains the following markup:

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Error</title>
</head>
<body>
    <h3>Unexpected error! Please contact the Administrator.</h3>
</body>
</html>

If you run the application you will see the Error view rendered in the browser like this:

 Error View
Error View

Overriding OnException Method

The try…catch technique discussed in the previous section allows you to trap errors at code level. However, this also means that you should identify all the places in your code that can potentially throw an exception. This may not be always possible and you may want to trap errors at the whole controller level. That’s what the OnException() method allows you to do. In this technique you override the OnException() method of the Controller base class and then write the exception handling code. This method is called whenever there is an unhandled error in the controller. The following code snippet shows how OnException() can be overridden in the HomeController class.

protected override void OnException(ExceptionContext filterContext)
{
  if (!filterContext.ExceptionHandled)
  {
    string controller = filterContext.RouteData.Values["controller"].ToString();
    string action = filterContext.RouteData.Values["action"].ToString();
    Exception ex = filterContext.Exception;
    //do something with these details here
    RedirectToAction("Error", "Home");
  }
}

The OnException() method receives the filterContext parameter that gives more information about the exception. Inside, you check the ExceptionHandled property to see whether the exception has been handled already by some other part of the controller or not. If this property returns false you go ahead and grab the controller and action name that caused the exception. Notice how RouteData.Values is used to retrieve the controller name and the action name. To get the actual Exception that was thrown you use the Exception property. Although not shown in the above code, you can use these pieces of information for logging or deciding a further course of action. In this example you simply redirect the control to the Error action method so that the Error view can be sent to the browser. The Error action method looks like this:

public ActionResult Error()
{
    return View();
}

Using HandleError Attribute

The [HandleError] attribute is possibly the simplest error handling technique. It requires that you decorate either the action methods or the controller with the [HandleError] attribute and create an Error view. If you place [HandleError] on top of action methods then any unhandled exceptions raised from that action cause the Error view to be sent to the browser. By default [HandleError] assumes that you have a view named Error either in the specific Views > <controller_name> folder or inside the Shared folder. You can also customize this view name using one of the properties of the [HandleError].

The following code shows how [HandleError] can be used with action methods as well as controllers:

[HandleError]
public ActionResult Index()
{
  ...
  return View();
}
[HandleError]
public class HomeController : Controller
{
  ...
}

If you add [HandleError] to the whole controller, unhandled exceptions arising in any of its action methods are handled by it and the Error view is displayed. Obviously, if you place [HandleError] at the controller level you don’t need to place it on top of each and every action method.

One tricky thing to remember is that [HandleError] requires custom errors enabled in the web.config. So, ensure that you have the following markup inside web.config:

<customErrors mode="On"></customErrors>

Before you run the application make sure to comment out the try…catch block as well as the OnException() method you wrote earlier and then test the working of the [HandleError] attribute.

The ErrorHandlerAttribute class has ExceptionType and View properties that can be used to customize the behavior of [HandleError]. The ExceptionType property can be used to specify a specific exception type that you wish to handle rather than generic exceptions. The View property can be used to specify a view acting as an error view.

Setting HandleError Attribute as a Global Filter

In the previous example you used the [HandleError] attribute at the action or controller level. You can register the same attribute class (HandleErrorAttribute) as a global error handling filter. To do so, open Global.asax and add this code in the Application_Start event handler:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    RouteConfig.RegisterRoutes(RouteTable.Routes);

    GlobalFilters.Filters.Add(new HandleErrorAttribute());
}

Here, you add HandleErrorAttribute to the GlobalFilters.Filters collection so as to set it as a global error handler. Any unhandled exception that takes place within the boundary of the MVC application will now be handled by this global error handler.

To test this global handler, comment out the [HandleError] attribute from the action or the controller and then run the application.

Handling Application_Error Event

The last exception handling technique discussed here is the Application_Error event. If you ever worked with ASP.NET Web Forms chances are you already know about this event. The Application_Error event is raised whenever  there is any unhandled exception in the application. That means an exception is not handled by any of the other techniques discussed earlier, it eventually gets bubbled up to the Application_Error event. Inside this event handler you can do tasks such as error logging and take some alternate path of execution. The following code shows how Application_Error can be added to Global.asax:

protected void Application_Error()
{
    Server.ClearError();
    Response.Redirect("/home/error");
}

The Application_Error event handler calls Server.ClearError() so as to convey to ASP.NET that the exception has been handled and that there is no longer an exception in the application. This way if you have set a custom error page in the web.config, it won’t be displayed. Then the code redirects the user to /home/error so that the Error view is displayed in the browser.

Summary

Error handling is an important consideration in any web application. ASP.NET MVC offers several error handling techniques in addition to try…catch that you can use. They include – overriding OnException() method, [HandleError] attribute, HandleErrorAttribute as a global filter and Application_Error event. Which of these techniques to use depends on the granularity of exception handling you need in an application.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read