An Exception to the Rule, Part 1

 

Rob Howard
Microsoft Corporation

July 23, 2001

Let's face it, when we write Web applications we occasionally have bugs (or in our more politically correct world, issues) within our code. Unfortunately, it always seems that the more senior the tester, that is CEO, CIO, board members, VC, and so on, the more frequent or serious these issues tend to be.

If you did any development what so ever in ASP, you'll recall a wonderfully helpful ASP error messages similar to:

HTTP 500.100 - Internal Server Error - ASP error
Error Type: Microsoft VBScript runtime (0x800A01A8)

As a developer your first reaction on seeing this error message is usually, "huh?" Next, you translate the error—an HTTP 500 Internal Server Error indicating that there is a problem with your ASP code. The next part is the error type, in this case it's the HRESULT code of 0x800A01AB, which, if you've got enough time on your hands you can grep MSDN (or use your Captain Crunch COM HRESULT secret decoder ring) and figure out that the code attempted to make a late bound call into a method that didn't exist.

Unfortunately for us, it is much more common for our senior testers to discover these issues. And, rather than filing a bug, these senior testers usually follow a less constructive route like picking up the phone and yelling at someone, usually your boss. The common question being, "why are you using Microsoft ASP if it causes errors? Really, it says it right there 'ASP error'."

ASP.NET allows us to easily handle these types of errors, and other runtime issues, in a much more structured and user-friendly manner. Our application can provide the developer with a rich, developer-specific error message, and our senior testers with beautiful and richly obscure messages that leave them delighted with the complexity of their system.

Additionally, you'll never see an ASP.NET error page (unless you write it yourself) that says ASP.NET error. We want to keep you honest, so we say Application error.

ASP.NET Exception Handling

ASP.NET allows us to build Web applications with any language following the Common Language Specification (CLS). Languages following the CLS, a specification submitted to ECMA for standardization, are capable of running on the Common Language Runtime (CLR). The CLR provides features such as garbage collection, secure code execution, and structured exception handling, and more.

We can take advantage of this language-independent, structured exception handling within our ASP.NET applications. Structured exception handling is done through try/catch/finally blocks, and forces us to deal with exceptions when they occur. If an exception is raised and we don't explicitly handle the exception, a general ASP.NET exception will occur, forcing the application to terminate without allowing the code to continue executing, resulting in an error page. This makes finding errors much, much easier.

This is very different from ASP code. With ASP code, we can call a Visual Basic 6 COM object's method, and if an error occurrs within the method, an error code (an HRESULT) is returned. However, it is our responsibility to check for the error, usually wrapping our code as follows (pseudocode):

<%
On Error Resume Next
...
dataObject.SomeBadMethod()
If Error.Number <> 0 Then
  Response.Write("An error occurred in the call to SomeBadMethod()")
  Response.End
End If
dataObject.SomeGoodMethod()
%>

If we didn't check for the Error.Number in the ASP pseudocode above,our code would continue executing after calling SomeBadMethod() and call right into SomeGoodMethod().

The concept of checking for errors still exists in ASP.NET, but we must deal with the error when it occurs:

<Script runat=server>
Public Sub Page_Load(sender As Object, e As EventArgs)
  ...
  Begin Try
    dataObject.SomeBadMethod()
  Catch
    ' Looks like an exception occurred, do work to either
    ' handle the error and clear the exception or fail
  End Try
  ...
End Sub
</Script>

The major difference is that we now handle the exceptions using Try/Catch/Finally exclusively. This is a good thing. When an error occurs, we get an instance of exception or possibly a derived, more specialized instanced, such as a NoNullAllowedException when attempting to insert a null value into a SQL table row that does not allow null value.

Exceptions provide us with detailed information about what occurred; for example stack trace, inner exception details, message, and additional details. This is much better than a COM HRESULT error code!

Unhandled Exceptions

Earlier we mentioned that ASP.NET, or rather the CLR, forces us to deal with an exception when an error occurs. We obviously don't write Try/Catch/Finally blocks around all of our code, and in the cases where we don't, ASP.NET still provides us with some facilities for handling the exception through two events:

  • Page_Error
  • Application_Error

Page and Application Error Event Handlers

The case exists where an exception is unexpected and does not occur within the bounds of a Try/Catch/Finally block. These exceptions are usually an indicator that something is substantially wrong with the application. That is, some abnormal error has occurred and there is no graceful way to get out of the error. In these situations, where we don't wrap the faulty code with a Try/Catch/Finally block, we still have the option to deal with the error at either the Page or Application level.

If we choose to deal with the error at the Page level, we must provide an implementation for the Page_Error event:

VB.NET

Public Sub Page_Error(sender As Object, e As EventArgs)
   ' Implementation here
End Sub

C#

public void Page_Error(Object sender, EventArgs e) {
   // Implementation here
}

This event is implemented within the Page that the error may occur within.

Alternatively, we can also implement the Application_Error event. Unlike the Page_Error event, the Application_Error event is implemented in global.asax and any unhandled exception that occurs within our application raises this event (if implemented):

VB.NET

Public Sub Application_Error(sender As Object, e As EventArgs)
   ' Implementation here
End Sub

C#

public void Application_Error(Object sender, EventArgs e) {
   // Implementation here
}

Within either of these events, we can run code such as sending an e-mail to the administrator or writing to the event log noting that an error occurred. In the next column, we'll discuss writing to the Windows Event Log.

In addition to running code to alert necessary persons to the exception within our application, we still have the opportunity to massage the exception—clearing it if necessary. We have access to the exception that occurred through the Server.GetLastError() method which returns an Exception class, and we can clear the exception using the API Server.ClearError().

Summary

Thanks to the Common Language Runtime, ASP.NET developers can now write structured error handling code using Try/Catch/Finally blocks. The CLR forces us to deal with the error when the error occurs, unlike ASP/COM where the error could go unchecked and potentially cause confusion later. In the next column we'll continue our discussion of ASP.NET error handling and look at the custom error page that ASP.NET generates. We'll also discuss how we can implement code that can write our exception details to the Windows Event Log.

Community Update

User Groups: I recently spoke at a great .NET user's group in St. Louis. On August 9th I'll be in Dallas speaking at another user's group. For more details see http://www.dnug.net/ViewGroup.aspx?id=21. Then on the 28th of August, I'll be speaking at a user's group in San Diego (I'll post a URL later). If you haven't already joined a .NET user's group, and you're serious about learning .NET, I definitely recommend it.

Conferences: The Microsoft ASP.NET Connections conference, September 18th–21st in Orlando, FL, will be a great conference that's completely focused on ASP.NET. For more details see http://www.asp-connections.com.

ASP.NET Magazine: For the last six months, the ASP.NET team has been working closely with the Informant Communications Group to put together a magazine dedicated to ASP.NET developers. The magazine will be called asp.netPRO and will be available later this fall. The ASP.NET team is going to be participating heavily in the magazine. We'll have an Ask the ASP.NET Team column and many other contributions from the ASP.NET team, as well as many of our incredibly talented third-party ASP.NET experts. For more details, as well as signing up for a free issue, see http://www.aspnetpro.com.

 

Rob Howard is a program manager for ASP.NET on the .NET Framework team. He spends whatever spare time he has either with his family or fly fishing in Eastern Washington.