
Introduction
No matter how great your application is at handling errors, there could still
be a time when an application throws an error that is unhandled. Default
web.config settings will have the customerrors tag set to Off, in which you get
the typical ASP.NET error page that is very helpful as a developer because it
usually points us in the right direction as to what went wrong. When a site
goes into production however, it is a good practice to at least have a static
page in which all errors go to that would carry over the sites branding
and present the user with a generic 'we are sorry...' message. It would be
great to take this a step farther and get the best of both worlds where
developers can get at specifics of the error without compromising the user
experience of the site (the site already bombed on them, the last thing they
want is an ugly confusing page). In this article, I will go over how to
implement global error handling for web applications that allows for technical
error information to get accessed while hiding technical jargon from the user.
Using the code
Sample project requirements:
-
The user can throw an exception
-
The system catches the error and stores it in a shared (static) class property
-
The user is redirected to an error page with consistent branding and a
user-friendly message
-
The user has the ability to get access to detailed information about the error
that was thrown
Configuring the Web.Config:
The customErrors
tag needs to be set to "On" and at the very least
the defaultRedirect
attribute needs to be set to the global error
handling page. You can also have varying redirects for different errors that
could occur but that is outside of the articles scope.
<customErrors mode="On" defaultRedirect="ErrorPage.aspx">
<error statusCode="404" redirect="LoginReg.aspx" />
</customErrors>
Configuring the Global.asax.vb:
There are a number of events that get fired at the application level that you
can tap into such as when the application is started or when a user's session
has ended. The event we will need for this project is when an error has
occurred at the application level. When the application error is thrown we can
store the error into a shared class property for later use. This could also be
stored in other ways as well but I chose to throw it into a class property.
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
Utilities.LastError = Server.GetLastError()
End Sub
Utilities Class:
The Utilities class is just a simple class with a shared exception property. In
reality this class could have a lot of miscellaneous methods used
throughout the application but for the article it is stripped down to only what
we need.
Public Class Utilities
Public Shared LastError As System.Exception
End Class
Global Error Page:
When we setup our web.config file we told ASP.NET to redirect to ErrorPage.aspx
when an error occurs. So we now need to create this page. This page will
display a friendly error message to the user. In real applications you would
have the consistent branding on this page, including perhaps a header,
navigation and footer control. All of this is left out, so the error page we
are displaying is rather plain but you get the idea. The page also grabs the
Exception that was thrown out of our Utilities class and binds it to some web
controls. Feel free to make this error page as robust as the business
requirements dictate. For example the page could pull over specific user
information out of the session, the current server variables and/or logging the
error specifics into the event log. I have left some lines of code in the
sample project for doing this but have them commented out because the sample
project doesn't actually have a user object nor does it include log4net.
Although I bind the information to controls on page_load, all of the
information has its visible property set to false to ensure the user isn't met
with technical information that doesn't mean anything to them. We then have
some way for a developer to show the technical information. This can be done by
checking for a querystring value, or a role the user is logged in as or by a
hidden button that they can click on. The sample project implements the later;
there is a show linkbutton control with a white font on the page which will
show the technical information if desired.
Private Sub Page_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
System.Reflection.MethodBase.GetCurrentMethod().DeclaringType)
If Not Page.IsPostBack Then
BindError()
BindServerVariables()
DataBind()
End If
End Sub
Private Sub BindError()
Dim t As New DataTable
t.Columns.Add(New DataColumn("Error Item"))
t.Columns.Add(New DataColumn("Error Value"))
With Utilities.LastError.InnerException
Dim r0 As DataRow = t.NewRow
r0(0) = "Message"
r0(1) = .Message
t.Rows.Add(r0)
Dim r1 As DataRow = t.NewRow
r1(0) = "StackTrace"
r1(1) = .StackTrace.Replace(vbCr, "<br>")
t.Rows.Add(r1)
Dim r2 As DataRow = t.NewRow
r2(0) = "Source"
r2(1) = .Source
t.Rows.Add(r2)
End With
dgLastError.DataSource = t
End Sub
Points of Interest
It should be noted that it is possible for a high traffic site to have to
errors be thrown at the same time, so there is a slight chance that the error
being displayed to User A is actually the error thrown by User B. This can be
accounted for by tracking the session id and storing the error differently, but
the chances are slim and to keep things simple, I have left this out.
http://log4net.sourceforge.net/ -
log4net is a logging API providing flexible and arbitrarily granular control
over log management and configuration.
History
- 1/28/2004 - article submitted