To read the article online, visit http://www.4GuysFromRolla.com/articles/091306-1.aspx

Gracefully Responding to Unhandled Exceptions - Processing Unhandled Exceptions

By Scott Mitchell


Introduction


When an unhandled exception occurs in an ASP.NET application, the visitor is, by default, shown either a "Runtime Error" or exception details page (which of the two pages is displayed depends upon the website's configuration and whether the visitor has come through localhost). Ideally, such esoteric error pages are only shown to developers; regular users should, instead, see a custom, user-friendly error page. In last week's article, Displaying User-Friendly Error Pages, we examined the steps to configure an ASP.NET web application to display custom, user-friendly web pages in response to unhandled exceptions.

The technique discussed in last week's article, however, uses a Response.Redirect() internally to redirect the user to the custom error page in the event of an unhandled exception. By redirecting the user, the context of the request is lost (since the redirect causes the browser to send a new request to the error page). Consequently, the custom, user-friendly error page lacks the ability to garner information about the error that just occurred. Such information might be desired in order to display a more informative error message.

When an unhandled exception bubbles up to the ASP.NET runtime, the application-level Error event fires. By creating an event handler for this event, we can access the error details, log the error, notify a developer, or send the user to the custom error page using Server.Transfer() (which will maintain the context and allow the custom error page to access the details of the unhandled exception).

In this article we'll examine how to create an event handler for the Error event and examine a free, open-source component for logging error details. Read on to learn more!

Exploring the Error Event


The HttpApplication class represents the methods, properties, and events common to all ASP.NET web applications. One event of this class is the Error event, which is fired whenever an unhandled exception bubbles up to the ASP.NET runtime. For example, imagine that in an ASP.NET web page you have code that connects to a database and runs a DELETE statement. If the database is offline when the page is visited, a SqlException will be thrown. If your code lacks any error handling logic, the exception will bubble up from your ASP.NET page's code-behind class to the ASP.NET runtime, at which point the Error event will be raised.

When an unhandled exception occurs, we typically want to do two things:

  • Display a custom, user-friendly message to the end user (see Displaying User-Friendly Error Pages)
  • "Process" the error - this may involve logging the error so that it can be inspected later; notifying a developer of the error by sending her an email; attempting to "fix" the problem
To "process" the error, we first need to create an event handler for the application Error event. The event handler can be created in Global.asax or handled through an HTTP Module. In the event handler, details of the unhandled exception can be retrieved using the Server.GetLastError() method. Once the error's details have been retrieved, the information can be logged, a developer can be notified, or control can be passed to a custom, user-friendly error page.

Creating an Error Event Handler in Global.asax


Global.asax is an optional file you can add to your web application's root directory to handle application- and session-level events. To create an event handler for the application's Error event, start by adding the Global.asax file to your project. Right-click on the project in Solution Explorer and choose Add New Item and choose the Global Application Class item type from the dialog box (see the screenshot below).

Add a Global.asax file to your project.

This will create a dummy Global.asax file with event handlers for the main application-level events - Error, Start and End - as well as event handlers for the session-level events Start and End. Since we're only concerned with the Error event, feel free to remove the other event handlers.

In the Error event handler, information about the exception that just occurred can be accessed by calling:

HttpContext.Current.Server.GetLastError()

This returns an Exception object that houses information about the error that bubbled up to the runtime, causing the Error event to fire in the first place. If the exception was caused from an ASP.NET page, the original exception is wrapped inside an HttpUnhandledException; the ASP.NET page-level exception can be accessed via the InnerException property, as we'll see shortly.

Imagine when an unhandled exception occurs we want to email a developer with details of the exception and then redirect the user to a custom error page, just like we examined in Displaying User-Friendly Error Pages. To accomplish this, we would configure the <customErrors> settings in Web.config appropriately, and then add the following code to the Error event handler in Global.asax:

Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
    'Get exception details
    Dim ex As Exception = HttpContext.Current.Server.GetLastError()

    If TypeOf ex Is HttpUnhandledException AndAlso ex.InnerException IsNot Nothing Then
        ex = ex.InnerException
    End If

    If ex IsNot Nothing Then
        Try
            'Email the administrator with information that an error has occurred
            '!!! UPDATE THIS VALUE TO YOUR EMAIL ADDRESS
            Const ToAndFromAddress As String = "email address"

            '(1) Create the MailMessage instance
            Dim mm As New System.Net.Mail.MailMessage(ToAndFromAddress, ToAndFromAddress)

            '(2) Assign the MailMessage's properties
            mm.Subject = "An unhandled exception occurred!"
            mm.Body = String.Format("An unhandled exception occurred:{0}Message: {1}{0}{0} Stack Trace:{0}{2}", System.Environment.NewLine, ex.Message, ex.StackTrace)
            mm.IsBodyHtml = False

            '(3) Create the SmtpClient object
            Dim smtp As New System.Net.Mail.SmtpClient

            '(4) Send the MailMessage (will use the Web.config settings)
            smtp.Send(mm)
        Catch
            'Whoops, some problem sending email!
            'Just send the user onto CustomErrorPage.aspx...
        End Try
    End If
End Sub

Pay particular attention to the first few lines of the event handler. Here we access the exception that just occurred (HttpContext.Current.Server.GetLastError()) and dig down into its InnerException property if needed. Then, the exception details are emailed to a specified email address using code taken verbatim from a previous article of mine, Sending Email in ASP.NET 2.0. (The SMTP relay server details used by this code can be found in Web.config.)

Say that a user visits a data-driven page and there's a problem in connecting to the database. The user will see the configured error page; behind the scenes, however, a developer will receive an email with the exception details:

The email with the exception details.

Transferring Control - Rather Than Redirecting - to the Custom Error Page


As examined in Displaying User-Friendly Error Pages, the <customErrors> section of Web.config can be configured to display custom, user-friendly error pages to visitors when an unhandled exception occurs. Unfortunately, the ASP.NET runtime redirects a user to the custom error page when an unhandled exception occurs. Specifically, the runtime calls Response.Redirect(customErrorPageUrl), which sends an HTTP 302 status to the browser, causing it to make a request for the specified URL (customErrorPageUrl). Since the redirect is a new web request, it has no connection or association with the request that had the unhandled exception. Consequently, calling Server.GetLastError() from the custom error page will return a null value.

Sometimes, however, we want to have information about the error in the custom error page. Perhaps the message displayed depends on the error that occurred, or we want to show exception details to authenticated visitors who are in a particular role. This can be achieved by calling Server.Transfer(customErrorPageUrl) in the Error event handler. From the docs, Server.Transfer(url) "terminates execution of the current page and begins execution of a new page using the specified URL path to the page." In essence, execution of the current request is handed off to another page on the server. There's no expicit redirect message sent back to the client - it all happens server-side.

Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
    '... Email details of exception to developer ...

    

Server.Transfer("~/SmartCustomErrorPage.aspx")


End Sub

With Server.Transfer(), the context is maintained so the custom error page can retrieve information about the unhandled exception using Server.GetLastError(). From the browser's perspective, there's no change in context either. That is, the browser's address bar will still show the URL of the web page that caused the unhandled exception, and not the URL Server.Transfer()ed to.

The download available at the end of this article presents a more intelligent custom error page (SmartCustomErrorPage.aspx). This error page inspects the exception details and bases its message on the type of exception that occurred.

Handling the Error Event with HTTP Modules


An HTTP Module is a managed component that can respond to events raised by the ASP.NET runtime. The event handler that responds to the Error event can, alternatively, be located in an HTTP Module. The benefit of using an HTTP Module is that it provides a "pluggable" component that can be added to an existing ASP.NET application by simply dropping the HTTP Module assembly in the /bin directory and adding a line or two of configuration to the application's Web.config.

Creating and configuring an HTTP Module for handling errors is beyond the scope of this article. Instead, let me heartily recommend a free, open-source library provided by Atif Aziz, called Error Logging Modules And Handlers, or ELMAH. ELMAH is composed of an HTTP Module for logging unhandled exceptions to a data store and an HTTP Handler for displaying exception details in a web page or RSS feed.

ELMAH can be downloaded from its Google Code page, http://code.google.com/p/elmah/. For a thorough discussion on setting up and installing ELMAH, check out Using HTTP Modules and Handlers to Create Pluggable ASP.NET Components. Setting up ELMAH takes 60 seconds or so, and provides a detailed log of exceptions, rich notification to developers, and user-friendly web pages for viewing exception details online. Highly recommended!

The download available at the end of this article uses the ELMAH library to log errors to a SQL Server 2005 Express Edition database...

Conclusion


When an unhandled exception occurs in a web application, we typically want to ensure that the end user sees a custom, user-friendly error page, and that the details of the exception are "processed." Processing the exception can differ for each application, but typically involves logging the exception and notifying a developer. A previous article, Displaying User-Friendly Error Pages, examined how to display a custom error page. In this article we saw how to create an event handler for the application Error event.

Rather than create your own exception logging and notification system, consider trying ELMAH, a free, open-source library that includes an HTTP Module for logging unhandled exceptions and another optional one for sending an email with the exception's details.

Happy Programming!

  • By Scott Mitchell


    Further Readings:


  • Using HTTP Modules and Handlers to Create Pluggable ASP.NET Components
  • The ELMAH Google Code Homepage (where you can download the code for ELMAH)
  • Attachments


  • Download example code for this article (in ZIP format)
  • Article Information
    Article Title: ASP.NET.Gracefully Responding to Unhandled Exceptions - Processing Unhandled Exceptions
    Article Author: Scott Mitchell
    Published Date: September 13, 2006
    Article URL: http://www.4GuysFromRolla.com/articles/091306-1.aspx


    Copyright 2014 QuinStreet Inc. All Rights Reserved.
    Legal Notices, Licensing, Permissions, Privacy Policy.
    Advertise | Newsletters | E-mail Offers