Error handling is a critical part of building enterprise level web applications,
for that matter, it is a huge page of any application. In one of last weeks article we discussed using the
Try...Catch...Finally statement to catch and respond to exceptions thrown during the page execution process.
In this article I will be introducing a new way to catch Page-Level exceptions by introducing the HandleError Method.
I will demonstrate what the firing order of the two are (try..catch..finally and HandleError, and demonstrate how you can try to catch exceptions in the
Pages HandleError event if your Try...Catch statement fails. Once the exception is caught I'll then demonstrate
how to log this exception and it's details to the Systems Event Log and finally redirect the clients browser to a
custom error page.
Catching Exceptions At The Page Level
The first thing we need to go over is the HandleError Method of the Page class.
The HandleError method throws an exception object when an error occurs while a page request is processing.
This method is protected so to use it you must use the overrides keyword if you want to make use of it.
Using this method does not mean you should not use Structured Error handling; it is just another avenue you can
take to catch potential un-handled exceptions.
A sample of how to implement the HandleError method can be found in Code Listing 1.1.
Code Listing 1.1
|
1. <%@ Page Language="vb" Strict="False" Explicit="False" %>
2. <html>
3. <head>
4. <script runat=server>
5. Sub Page_Load(Sender As System.Object, _
6. E As System.EventArgs)
7. Dim obj As System.Object
8. Call obj.CallFakeMethod()
9. End Sub
10. Protected Overrides Sub HandleError( _
11. ByVal ErrInfo As System.Exception)
12. Response.Write(ErrInfo. StackTrace)
13. End Sub
14. </script>
15. </head>
16. <body>
17. </body>
18. </html>
|
The HandleError method has to be written as it is in Line 10-11. If this page is executed there will be an exception thrown by the calling of a fake method on line 8. The HandleError method will catch this error and I write the stack trace out to the page. The rendered page can be viewed in the figure below (Figure 1.1) Notice that error is LateBinding.LateCall.
A couple other things worth noting about this code example are the attributes listed in the @Page directive. The Language attribute is pretty obvious, but there are two attributes listed that you may have not seen before; the first is the Strict attribute. Setting this attribute to True or False will set Option Strict On or Off for the Page. The second attribute is Explicit, this sets Option Explicit On or Off for the Page.
Figure 1.1
Using HandleError With the TryCatchFinally Statement
Using the HandleError method is great for catching errors, but you shouldn't just use this method. Combining it with the TryCatchFinally statement increases your odds of recovery from the exception dramatically. In the following example (Code Listing 1.2) I'll show you one way to use the two together effectively.
Code Listing 1.2
|
<%@ Page Language="vb" Strict="False" Explicit="False" %>
<html>
<head>
<script runat=server>
Sub Page_Load(Sender As System.Object, _
E As System.EventArgs)
Try
Dim obj As System.Object
Call obj.CallFakeMethod()
Catch Ex As System.Exception
'First Method Call Didn't Work Try
'A New Object Or Method Call
Dim intTest As System.Integer
intTest = "This Is A String"
'oh oh Error Occurs Again, no more exception handling!!
End Try
End Sub
Protected Overrides Sub HandleError( _
ByVal ErrInfo As System.Exception)
Response.Write(ErrInfo.StackTrace)
'HandleError takes over
End Sub
</script>
</head>
<body>
</body>
</html>
|
In Code Listing 1.2 within the Try block I am calling the LateBound object's fake method again.
When the exception is thrown I effectively catch it in the Catch block.
Here I create a new Integer object, but instead of assigning an integer value to it I assign a string,
which will throw an exception. With no more exception handling available inline, I depend on the Page's
HandleError method to catch this exception and deal with it. In this situation I simple call a Response.Write
method, but I am sure you can think of a better use, one might be to log the error information somewhere which
brings us to the next section, logging this information to the System Event Log. Figure 1.2 is a
screen shot of the rendered page.
Figure 1.2
Logging Exceptions to the System Event Log
In this section we will be combining all the previous methods of error handling, and adding two more. I'll demonstrate how to log exception data to the System Event Log, and how to use some of ASP.NET's built in functionality to then redirect the users browser to a user-friendly page. Because if we are at the point of logging the exception data to the Event Log, then this is probably not going to be a recoverable exception.
Before I get into the code I want to talk a little about how you enable logging to the System's Event Viewer. The EventLog class is what makes it all possible, the EventLog class is found in the System.Diagnostics Namespace. Beyond adding new log items, the EventLog class enables you to manipulate the logs however you wish, although this is out of the scope of this article.
You create the EventLog object by a simple declaration.
Dim eLog As New System.Diagnostics.EventLog()
If you only wish to write to the event log you must do two things. The first is to create or specify a Source. The second is to call the WriteEntry method. The source would be your application name, by default, if you create a source that is new then your log entry will be written to the Application Log. To WriteEntry method does the actual writing to the Event Log. In this example I will demonstrate how to create your own Application Log, i.e. (ASPDOTNET_Errors) and Source object. In Code Listing 1.3 I'll demonstrate how to combine the TryCatchFinally, the Pages HandleError Method, EventLog class, and the web applications section of the Config.Web file to create a page that will attempt to try a risky operation, and it will throw an exception object which will be caught by our catch statement, which in turn will throw another exception which will be caught by the pages HandleError method of the Page object. It will log the exception in the Systems Event Log, and then throws another exception that will be caught by the application object and be redirected the user-friendly error page specified in the Config.Web.
Code Listing 1.3
[Web Form]
|
<%@ Page Language="vb" Strict="False" Explicit="False" %>
<html>
<head>
<script runat=server>
Sub Page_Load(Sender As System.Object, _
E As System.EventArgs)
Try
Dim obj As System.Object
obj.CallFakeMethod()
Catch ex As System.Exception
'Try To Recover
'Cant Recover, Throw Error Again
Throw ex
End Try
End Sub
Protected Overrides Sub HandleError( _
ByVal ErrInfo As System.Exception)
Dim Log As New System.Diagnostics.EventLog()
Dim LogName As System.String = "ASPDOTNET_Errors"
Dim LogSource As System.String = "ASPNextGen_Web_Errors"
Dim LogErrorMessage As System.String = ErrInfo.Message & _
" " & ErrInfo.StackTrace
'//Check If Event Log Exsists, If Not Create It
With Log
If Not .SourceExists(LogSource) Then
.CreateEventSource(LogSource, LogName)
End If
'//Write Log Entry
.Source = LogSource
.WriteEntry(LogErrorMessage, _
System.Diagnostics.EventLogEntryType.Error)
End With
Throw ErrInfo
End Sub
</script>
</head>
<body>
</body>
</html>
|
[Config.Web]
|
<customerrors mode="On" defaultredirect="mycustompage.htm"/>
|
Break It Down
The Page_Load code is pretty basic I am once again creating and throwing a made up object's method. Once the exception is thrown I catch it in the Catch block, I tried really hard to recover from it and couldn't so I throw the error again so the Page objects HandleError method can do some work for me.
Listing 1.4
|
1. Dim Log As New System.Diagnostics.EventLog()
2. Dim LogName As System.String = "ASPDOTNET_Errors"
3. Dim LogSource As System.String = "ASPNextGen_Web_Errors"
4. Dim LogErrorMessage As System.String = ErrInfo.Message & _
5. " " & ErrInfo.StackTrace
6. '//Check If Event Log Exsists, If Not Create It
7. With Log
8. If Not .SourceExists(LogSource) Then
9. .CreateEventSource(LogSource, LogName)
10. End If
11. '//Write Log Entry
12. .Source = LogSource
13. .WriteEntry(LogErrorMessage, _
14. System.Diagnostics.EventLogEntryType.Error)
15. End With
|
Listing 1.4 contains the code I use to write to the Event Log. On Line 1 I create the EventLog object and assign it to a variable named Log. Then I create three more variables. LogName holds the name of the log I want to create or use, I use ASPDOTNET_Errors. LogSource holds the source property I'll be using, I use ASPNextGen_Web_Errors. LogErrorMessage is what I am going to write to the Log entry, in this I am writing the error message and the stack trace of the exception that occurred.
In lines 8 - 10 I check to see if the source exists (ASPNextGen_Web_Errors), if the source does exists my error log entry will be written to the Log File that that source name is associated with. If it doesn't exist it will be created along with my new Log File by calling the CreateEventSource method of the EventLog class. Lines 12-14 I do the actual writing to the ASPDOTNET_Errors log. On Line 12 I specify the source as LogSource, and then I call the WriteEntry method I pass in two objects, the LogErrorMessage and a new object the EventLogEntryType. The EventLogEntryType specifies the event type for this event entry. You do not have to specify a type, but I think it is good form. There are 5 different types of entries in the EventLogEntryType enumeration. The one we use, Error; also, FailureAudit, Information (default), SuccessAudit, and Warning. So now what? Well if we stop there our user will see a page with errors on it. Well that's were our entry in the Config.Web comes in handy. Notice in List 1.3 after we write the error to the Log we throw the exception again, I do this so the runtime will now redirect the user to the correct page we have set up in our Config.Web. In this case a generic page mycustompage.htm
So did our error really get written to the even log? Figures 1.3 and 1.4 are screenshots of our new Log File and the new Log entry in it.
Figure 1.3
Figure 1.4
Conclusion
There are so many ways to catch and handle errors with ASP.NET and the .NET framework that it is hard to keep
track of all them. The introduction of structured error handling alone blows traditional ASP error handling
methods away. In this article I briefly discussed how to use the HandleError method of the Page object to handle
errors. How to combine using a TryCatch statement with the HandleError method, and finally how to Log
exceptions to the EventLog. As more information is released, and enhancements made to .NET more articles on
the different methods of error handling will be released by ASPNextGen. Next weeks article will go
one step further and demonstrate how to use the global.asax file to catch application exceptions using
the Application_Error event.