Click here to monitor SSC
  • Av rating:
  • Total votes: 57
  • Total comments: 3
Dino Esposito

Handling Errors Effectively in ASP.NET MVC

10 April 2014

ASP.NET MVC gives you more options in the way that you handle exceptions. Error handling isn't intrinsically exciting, but there are many ways of avoiding the classic yellow page of death, even getting ELMAH to manage error handling for you.

Years ago,  ASP.NET’s error handling was one of the major things that made me wonder if ASP.NET MVC could give me something that ASP.NET Web Forms couldn’t. Web Forms is based on pages; so if something goes wrong, all that you can do is to redirect the user to another page and explain what the error was or just be generically sorry. ASP.NET Web Forms allow you to map an error page for each possible HTTP status code. You control the mapping through the <customErrors> section of the web.config file.

Because of the  different architecture of the view in ASP.NET MVC,  it  is possible to save the redirect command and then programmatically switch to an error view in the context of the same request. You have this in addition to the regular page-based error handling mechanism. I wouldn’t use HTTP code redirects in ASP.NET MVC; but only because more flexible solutions are possible.

Generally speaking, error handling in ASP.NET MVC is mainly necessary to handle program and route exceptions. Program exceptions refer to catching errors in controllers and in any code you may have in Razor views. Route exceptions refer to missing links and invalid URLs.

Program Exceptions

Any stack trace you can have out of an ASP.NET MVC application originates from a method call in a controller class. The controller class, therefore, is where any exceptions in your ASP.NET MVC code can be trapped. You can do that in a number of equivalent ways. For example, you can have a try/catch block surrounding the entire method body. It works, but it’s ugly to see too. A better option is probably to override the OnException method from the base Controller class. Yet another option is using the HandleError attribute at the controller class level. Better yet, the HandleError attribute—which is ultimately an action filter—can be set globally on just every controllers and actions you can have.

At the end of the day, an effective strategy for error handling is based on the following pillars:

  • All classes down the controller level just throw exceptions when something goes wrong.
  • Some of these classes, in some of their methods, may attempt to catch some of the exceptions but mostly when a given exception is intended to be swallowed or turned into some other exception.
  • At the application level you use the HandleError global filter to catch whatever bubbles up.

Swallowing exceptions is in general a dangerous practice; but in the end it is not more dangerous than crossing the street when it’s red but there are no cars in sight. It works well as long as it doesn’t become a common practice and as long as it’s applied with a grain, or maybe two, of salt. Swallowing an exception is fine for example if your code is trying to call an external HTTP endpoint and the call times out or fails for whatever reason. In this case it might be acceptable that the routine that takes care of the call just hides the actual HTTP status code and packs the event as a special case of the regular response. Here’s an example taken from a data access repository class:

public Order FindByCustomerAndId(int id, string customerId)
{
    using (var db = new MyAppEntities())
    {
        try
        {
            var order = (from o in db.Orders
                            where o.OrderId == id && o.Buyer.CustomerId == customerId
                            select o).Single();
            return order;
        }
        catch (InvalidOperationException)
        {
            return new NullOrder();
        }
    }
}

In LINQ, the Single method just throws an exception if the response includes any number of items different from one. The internal try/catch block swallows the exception and returns a special version of the Order type that just evaluates to NULL.

The NullOrder class is an instance of the Special Case pattern and has the merit of not killing polymorphism in code as NULL would do. The caller of the aforementioned method will have then the following skeleton:

var order = _orderRepository.FindByCustomerAndId(orderId, customerId);
if (order is NullOrder)
{
  ...
}

There are a few guidelines you might want to adhere to when it comes to handling exceptions. The first aspect to consider is that the catch block is quite expensive and raises a peak of CPU usage when your code gets into it. For this reason, over-catching may end up affecting the overall performance of the application. It’s probably not a big deal if your code is frontend; but for server-side code scaling up the performance of the entire system it might become problematic.

A guideline from the .NET Framework team is that you never throw an exception using the System.Exception class. You should use more specific exception types whether built-in types such as InvalidOperationException and NullReferenceException or your own application specific types. On the other hand, you should also resist the temptation of having your own exception types sprinkled everywhere and even replacing analogous .NET Framework native types.

When it comes to exceptions, you should be very specific about the exception-type that you pick up and should also create instances providing as much information as possible. For example, ArgumentNullException is way more specific than ArgumentException. If the problem consists in an unexpected NULL parameter then you should go for ArgumentNullException. Furthermore, be aware that any exceptions come with a message. Provide details within the message as the message itself is targeted to developers. Another parameter of exception types that is often neglected is the name of the parameter where the exception occurred—mention it every time. It can be a lifesaver sometimes.

The OnException Method

In ASP.NET MVC, any method of any controller runs under the aegis of a system component known as the action invoker. The invoker runs all the code within a try/catch block and simply re-throws a thread-abort exception. For all other exceptions, instead, it goes through the list of registered action filters and gives each a chance to recover from the exception. At the end of the loop, if the exception has not been marked as handled, the exception originally caught is re-thrown. What happens next depends on whether you have other mechanism of exception handling set to watch over the entire application. If none is in place, which is the default, users will experience the ASP.NET classic yellow page of death or any other error page you arranged.

An action filter enabled to handle exceptions can be a separate class defined as an action filter (inherit from the ActionFilter class) or it can simply be a controller class that overrides the OnException method. This method is always invoked when an unhandled exception occurs in the course of the action method.

protected override void OnException(ExceptionContext filterContext)
{
   ...
}

It’s important to be aware that no exception that originates outside the controller will be caught by OnException. An excellent example of an exception not being caught by OnException is a ‘null reference’ exception that results in the model-binding layer. Another example is ‘route not-found’ exception.

The code in OnException has the power of controlling the entire response for the request that failed. As shown above, the method receives a parameter of type ExceptionContext which has an ActionResult property named Result. This property just refers to the next view or result past the method. If you set the Result property you can control the next screen; if you omit setting any result, then the user will see just a blank screen. Here’s a typical implementation of OnException:

protected override void OnException(ExceptionContext filterContext)
{
    // Let other exceptions just go unhandled
    if (filterContext.Exception is InvalidOperationException)
    {
        // Default view is "error"
        filterContext.SwitchToErrorView();
    }
}

The SwitchToErrorView method is an extension method for the ExceptionContext class with the following signature:

public static void SwitchToErrorView(this ExceptionContext context,
                                     String view = "error", String master = "")

As you can see, you’re allowed to indicate the next view and even its layout.

The HandleError Attribute

If you don’t like the explicit override of OnException you can decorate the class (or just individual methods) with the HandleError attribute. 

[HandleError]
public class HomeController
{
    ...
}

As mentioned, HandleError is an action filter and not a plain attribute carrying just a bunch of metadata. In particular, HandleError implements the IExceptionFilter interface:

public interface IExceptionFilter
{
   void OnException(ExceptionContext filterContext);
}

Internally, HandleError implements OnException using a piece of code very similar to the SwitchToErrorView method discussed earlier. A minor difference is that HandleError doesn’t trap any exceptions resulting from child actions. Properties on the attribute lets you select the exceptions to trap and views to redirect to.

[HandleError(ExceptionType=typeof(ArgumentException), View="generic")]

Each method can have multiple occurrences of the attribute, one for each exception you’re interested in. By default, also HandleError redirects to the same view named error we considered earlier. Note that such a view is purposely created by the ASP.NET MVC templates in Visual Studio.

When using HandleError at development time, it’s crucial to be aware that the attribute doesn’t have any effect unless you enable custom errors at the application level:

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

When you go live, remote users will correctly receive the selected error page regardless. To test the feature, though, you need to change the configuration file.

HandleError can be automatically applied to any method of any controller class by registering it as a global filter in global.asax:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        RegisterGlobalFilters(GlobalFilters.Filters);
        ...
    }
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }
}

Global filters are automatically added to the list of filters before the action invoker calls out any action method.

All Other Possible Errors

An error can always find its way to the user. For this reason, we’ve been given the Application_Error method in global.asax  ince the very first version of the ASP.NET runtime. It is just there to handle any possible errors that passed through try/catch blocks. The Error event fires whenever an unhandled exception reaches the outermost shell of ASP.NET code.  It’s the final call for developer’s code before the yellow screen of death.

You could do something useful in this event handler, such as sending an email or writing to the event log. 

void Application_Error(Object sender, EventArgs e)
{
    var exception = Server.GetLastError();
    if (exception == null)
        return;
    var mail = new MailMessage { From = new MailAddress("automated@contoso.com") };
    mail.To.Add(new MailAddress("administrator@contoso.com"));
    mail.Subject = "Site Error at " + DateTime.Now;
    mail.Body = "Error Description: " + exception.Message;
    var server = new SmtpClient { Host = "your.smtp.server" };
    server.Send(mail);

    // Clear the error
    Server.ClearError();

    // Redirect to a landing page
    Response.Redirect("home/landing");
}

While you can always write the Error handler yourself, ASP.NET developers often use ELMAH. At the very end of the day, ELMAH is an HTTP module that, once configured, intercepts the Error event at the application level and logs it according to the configuration of a number of back-end repositories. The bottom line is that with ELMAH you can handle errors in many more ways and change /add actions with limited work; and without writing much code yourself.  ELMAH also offers some nice facilities, such as a web page you can use to view all recorded exceptions and drill down into each of them.  

ELMAH is an open-source project available at http://code.google.com/p/elmah. It is so popular that it counts a number of extensions, mostly in the area of repositories. To integrate it in your applications the easiest path you can take is the Nuget package you find at http://www.nuget.org/packages/elmah/1.2.2.

Summary

Error handling is one of the most bothersome parts of software development. One of the reasons that developers avoid it is that it doesn’t seem to require much creativity. In this regard, I think that ELMAH is emblematic. Nearly any developers knows that an HTTP module could do the trick of saving rewriting the same code over and over again to send emails and log errors on ASP.NET sites. And I guess as many developers had, in the past, a thought crossing their minds about writing a sort of simple but effective infrastructure for error handling and reporting. That’s just what ELMAH is—and that’s what ASP.NET developers need. Oh well, in addition to ad hoc try/catch blocks in the code.

Dino Esposito

Author profile:

A long-time trainer and consultant, Dino is the author of many popular books for Microsoft Press for .NET developers.including “Architecting Mobile Solutions for the Enterprise“ and “Programming ASP.NET MVC” both for Microsoft Press. CTO of Crionet, a firm specializing in Web-based and mobile solutions for sport events across Europe (http://www.crionet.com), at the moment Dino is also technical evangelist for JetBrains, where he focuses on Android and Kotlin development, and member of the team that manages WURFL—the database of mobile devices used by organizations such as Google and Facebook. Follow Dino through his blog at http://software2cents.wordpress.com or at http://twitter.com/despos. Check out Dino’s latest book “Microsoft .NET: Architecting Applications for the Enterprise”.

Search for other articles by Dino Esposito

Rate this article:   Avg rating: from a total of 57 votes.


Poor

OK

Good

Great

Must read
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: Excellent Information
Posted by: Jean (not signed in)
Posted on: Thursday, April 24, 2014 at 2:30 PM
Message: Very good way and simple to implement exception in asp.net mvc.

Subject: Logging?
Posted by: David Kemp (not signed in)
Posted on: Friday, April 25, 2014 at 7:02 AM
Message: I know you mention ELMAH at the end of the article, but, in your first example, you catch and ignore the exception.
I've lost count of the number of times I've seen this style of handling exceptions in production and it being related to unstable or buggy code.
Please at least mention the fact that you really should log all exceptions that you handle in this way, as sooner or later they will bite you in production.
(In this specific example, you should probably use SingleOrDefault and the null coalescing operator)

Subject: Exceptionless
Posted by: niemyjski (view profile)
Posted on: Tuesday, May 6, 2014 at 12:59 PM
Message: I'd also recommend checking out Exceptionless! It's way better than ELMAH and is also open source (https://github.com/exceptionless/Exceptionless).

 

Top Rated

Rethinking the Practicalities of Recursion
 We all love recursion right up to the point of actually using it in production code. Why? Recursion... Read more...

Acceptance Testing with FitNesse: Multiplicities and Comparisons
 FitNesse is one of the most popular tools for unit testing since it is designed with a Wiki-style... Read more...

Acceptance Testing with FitNesse: Documentation and Infrastructure
 FitNesse is a popular general-purpose wiki-based framework for writing acceptance tests for software... Read more...

Prototyping Desktop Deblector
 Deblector is an open-source debugging add-in for .NET Reflector; the Reflector team investigated... Read more...

.NET Reflector Through the Looking Glass: The Pudding
 There a number of ways in which Reflector, either by itself or with an Addin, allows you to analyse... Read more...

Most Viewed

A Complete URL Rewriting Solution for ASP.NET 2.0
 Ever wondered whether it's possible to create neater URLS, free of bulky Query String parameters?... Read more...

Visual Studio Setup - projects and custom actions
 This article describes the kinds of custom actions that can be used in your Visual Studio setup project. Read more...

.NET Application Architecture: the Data Access Layer
 Find out how to design a robust data access layer for your .NET applications. Read more...

Calling Cross Domain Web Services in AJAX
 The latest craze for mashups involves making cross-domain calls to Web Services from APIs made publicly... Read more...

Build and Deploy a .NET COM Assembly
 Phil Wilson demonstrates how to build and deploy a .NET COM assembly using best practices, and how to... Read more...

Why Join

Over 400,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.