Displaying Gentle Error Messages with ASP.NET
| | Learn how to display gentler error messages when errors are encountered while developing ASP.NET applications. In this article, Stephen Walther shows you how you can modify the default ASP.NET error page so that it displays motivational sounds, messages, and pictures. |
| Stephen Walther Difficulty: Easy Time Required: Less than 1 hour Cost: Free Hardware: |
I fly out to companies and teach workshops on building ASP.NET applications to developers new to the ASP.NET framework. Typically a developer starts out excited to learn something new. Unfortunately, a developer’s first experience of the ASP.NET framework is often the error page -- the angry page with the yellow background and the red error message.
When a novice developer encounters their first error message, I try to be reassuring. I explain that errors are normal and good and that an ASP.NET developer will view the error page at least half a billion times while developing a project of any reasonable complexity. After all, the mark of a good development framework is that it throws a lot of errors at you. A framework that throws a lot of errors prevents you from doing something stupid and deploying a buggy application into production.
However, the question remains: why does the ASP.NET Framework seem so angry? Why the harsh yellow background and bold red error messages (see Figure 1)? Why can’t the error page be more gentle? In other words, why can’t the error page be more Myst than Halo?
Figure 1 -- Angry ASP.NET Error Page
This question brings us to the topic of this article. The nice thing about the ASP.NET Framework is that you can customize just about any aspect of the framework in any way that you want. This month’s project is to build a friendlier, gentler ASP.NET error page. Our error page will use nurturing colors such as blues and grays instead of yellows and reds. Furthermore, our custom error page will randomly display different motivational messages and pictures and play different soothing background sounds.
The Visual Studio Installer file that accompanies this article includes a new VB.NET and C# Project Template named Friendly Errors. If you choose this template when creating a new project, the default error message page will be replaced with a custom error message page (see Figure 2).
Figure 2 -- Gentle ASP.NET Error Page
Handling Application Exceptions
I should admit up front that I thought that this would be an extremely easy project. The ASP.NET Framework enables you to easily create a custom error page by adding a <customErrors> section to your application’s Web configuration (Web.config) file. For example, adding the following Web.config file to the root of your application causes a page named CustomError.aspx to be displayed when there is any error:
<configuration>
<system.web>
<customErrors mode="On" defaultRedirect="~/CustomError.aspx" />
</system.web>
</configuration>
Adding this configuration file to your application works great. Whenever there is an error, the user is redirected to the CustomError.aspx page automatically. You can display any message in this page that you want. For example, you can display the message “This site is currently undergoing scheduled maintenance”.
Unfortunately, however, there is no way in the CustomError.aspx page to get information about the error that caused the redirect to the CustomError.aspx page. You can display a message, but you cannot display detailed error information.
Therefore, in this project, I took another approach. Instead of using the <customErrors> section, I handled the Application Error event. This event is raised whenever there is any unhandled exception in an application. In other words, if any code within a page or a component raises an exception, and the code is not wrapped by a Try...Catch block, then the Application Error event is raised.
There are two ways that you can handle the Application Error event. You can handle the event in the Global.asax file or you can handle the event by creating a custom HTTP Module. I handled the Application Error event in a module named FriendlyErrorsModule. The code for the FriendlyErrorsModule is contained in Listing 1.
Listing 1 -- FriendlyErrorModule.cs
using System;
using System.Web;
using System.Web.Configuration;
namespace Superexpert
{
/// <summary>
/// Handles all unhanlded exceptions in an application
/// </summary>
public class FriendlyErrorsModule : IHttpModule
{
public void Init(HttpApplication app)
{
app.Error += new EventHandler(app_Error);
}
/// <summary>
/// When debug mode is enabled, display
/// friendly error page
/// </summary>
void app_Error(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
HttpContext context = app.Context;
Exception error = context.Server.GetLastError().GetBaseException();
context.Response.Clear();
CompilationSection compilationConfig = (CompilationSection)WebConfigurationManager.GetWebApplicationSection("system.web/compilation");
if (compilationConfig.Debug)
context.Server.Transfer("~/FriendlyErrors/Debug.aspx");
else
context.Server.Transfer("~/FriendlyErrors/Public.aspx");
}
public void Dispose()
{
}
}
}
Listing 1 -- FriendlyErrorModule.vb
Imports System
Imports System.Web
Imports System.Web.Configuration
Namespace Superexpert
''' <summary>
''' Handles all unhanlded exceptions in an application
''' </summary>
Public Class FriendlyErrorsModule
Implements IHttpModule
Public Sub Init(ByVal app As HttpApplication) Implements IHttpModule.Init
AddHandler app.Error, AddressOf app_Error
End Sub
''' <summary>
''' When debug mode is enabled, display
''' friendly error page
''' </summary>
Sub app_Error(ByVal sender As Object, ByVal e As EventArgs)
Dim app As HttpApplication = CType(sender, HttpApplication)
Dim context As HttpContext = app.Context
context.Response.Clear()
Dim compilationConfig As CompilationSection = CType(WebConfigurationManager.GetWebApplicationSection("system.web/compilation"), CompilationSection)
If (compilationConfig.Debug) Then
context.Server.Transfer("~/FriendlyErrors/Debug.aspx")
Else
context.Server.Transfer("~/FriendlyErrors/Public.aspx")
End If
End Sub
Public Sub Dispose() Implements IHttpModule.Dispose
End Sub
End Class
End Namespace
The class in Listing 1 implements the IHttpModule interface. This interface has two methods that you must implement: Init() and Dispose(). In Listing 1, the Init() method is used to wire the Application Error event to a handler named app_Error.
The app_Error handler checks whether debugging is enabled in the Web.config file. If debugging is enabled, then the user is transferred to a page named Debug.aspx whenever there is an error. Otherwise, if debugging is not enabled, the user is transferred to a page named Public.aspx.
Creating a Custom Error Page
The last part of this project involved creating the error page itself. The custom error page is named Debug.aspx. This page does a number of different things.
First, the page displays detailed error information. To get detailed error information, you must create an instance of the System.Diagnostics.StackTrace class. You can take advantage of the StackTrace class to get the name of the page associated with the error as well as the source code that caused the error.
The Friendly Errors Project Template includes a class named FriendlyError. This class includes methods for retrieving detailed error information. the Debug.aspx page uses the FriendlyError class behind the scenes.
The Debug.aspx page also displays a random picture. You can modify the pictures that the Debug.aspx page displays by modifying the contents of the FriendlyErrors/Pictures folder. The Debug.aspx page randomly displays one picture from this folder.
You can add any pictures to this folder that you want. For example, you can add baby pictures and motivational pictures of your favorite foods or persons.
The Debug.aspx page also displays a random motivational message. You can modify the motivational messages displayed by the Debug.aspx page by modifying the FriendlyErrors/Messages/Messages.xml file. This XML file contains a list of <message> elements that contain each of the motivational messages.
Finally, the Debug.aspx page plays a background sound. The page uses the <object> tag so that the background sound will play correctly in both Microsoft Internet Explorer and Mozilla Firefox. You can customize the background sounds used by the Debug.aspx page by modifying the contents of the FriendlyErrors/Sounds folder. The Debug.aspx page randomly plays a sound from this folder.
Again, you can add any sound file to this folder that you please. By default, the folder contains soothing chime sounds. If you want, you could add drop other motivational sounds into this folder such as Homer Simpson saying Doh!
Testing the Custom Error Page
You’ll see the custom error page, Debug.aspx, whenever there is any unhandled exception in your project. Don’t worry, unless you are the perfect programmer, you’ll see the Debug.aspx pretty quickly when developing an application when using the Friendly Errors Project Template.
If you insist on intentionally creating an error in your application, then you can use the page in Listing 2. Both the C# and VB.NET versions of this page cause an exception that causes the Debug.aspx page to be displayed.
Listing 2 -- BadPage.aspx (C#)
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
void Page_Load()
{
int zero = 0;
Response.Write(1/zero);
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Bad Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
I am a bad page
</div>
</form>
</body>
</html>
Listing 2 -- BadPage.aspx (VB.NET)
<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
Sub Page_Load()
Dim Blow
Blow.Up()
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Bad Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
I am a bad page
</div>
</form>
</body>
</html>
Conclusion
The purpose of this month’s column is to demonstrate that you can customize just about any aspect of the ASP.NET Framework: including the default error page. If you are going to spend countless hours staring at one particular page (and I’ve spent an embarrassing number of hours of my life staring at the error page) then you might as well make the experience pleasant.