User:Clickdictionary/Sandbox

Web Application Error Handling in ASP.NET

Introduction All applications should have error handling. This we all know. We can't always be notified of an unhandled error (and usually aren't) when one occurs on a client's machine. The advantage we have on the Web is that we can always be notified when an unhandled error occurs. With the advent of ASP.NET, there are some great new ways to handle errors. There are some differences in .NET in not only how to handle the error, but how the information is provided to you. For example, classic ASP uses Server.GetLastError to return an ASPError object. You can and should still use Server.GetLastError in .NET, but this now returns a type System.Exception. I must give Microsoft credit for making almost everything consistent in .NET, which is quite a welcome change.

The Problem

Errors will occur in our applications. We try to trap for most errors using try-catch blocks (or the only possibility in tradional ASP 'on error resume next'); however, we usually don't cover every possible exception. What happens when an unhandled error occurs? Usually the user is brought to IIS's default error pages (usually located in c:\winnt\help\iishelp\common). The downsides are you have no idea when this occurs and the page doesn't have your site's look and feel. Errors are a development fact, but we strive to eliminate or handle them gracefully. With this in mind, we need to know: 1.	When an error occurs 2.	Where it occurred 3.	What the error is Having a central location such as the event log, database or some other log file to log errors is essential for debugging this problem later (I call this forensics debugging).

IIS provides great error-handling capabilities. There are some problems with these though. Sometimes we know an error will occur, and we can't always trap for it in a nice way without overriding the site's (done in the IIS custom errors Page; see the article mentioned above) default error redirection page. For example, upon access to a resource that requires authentication, we may need to redirect to an application's login page. Also, a very common problem exists with Web hosts. If you have a hosted Web site, you usually have no control over its IIS configuration. Thus, setting up custom error pages can be next to impossible in traditional ASP. This is elimiated with ASP.NET, as you will learn as you read on.

The Solution For such a list of problems, the solution is actually pretty simple. There are three places in ASP.NET to define what happens to these unhandled errors. 1.	In the web.config file's customErrors section. 2.	In the global.asax file's Application_Error sub. 3.	On the aspx or associated codebehind page in the Page_Error sub. The actual order of error handling events is as follows: 1.	On the Page itself, in the Page_Error sub (this is default, you can name it anything because it specificed Handles MyBase.Error) 2.	The global.asax Application_Error sub 3.	The web.config file Note: To cancel the bubbling up of the error at anytime for the Page_Error or Application_Error, call the "Server.ClearError" function in your sub. Each method has its own uses, as I will explain. When an exception occurs in your application, it should be an object inherited from type System.Exception, and as such will have the following public members:

Using the Page_Error or OnError sub The first line of defense in error handling happens at the page level. You can override the MyBase.Error sub as such: (Visual Studio will complete the code if you click either the Overrides or BaseClass events in the editor). The two functions you can use (one or the other, both will not work as only one will get called) Private Sub Page_Error(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Error End Sub

Or you can use this one: Protected Overrides Sub OnError(ByVal e As System.EventArgs)

End Sub

Handling errors in these subs is a simple process. Just call Server.GetLastError to return the error. If you want to redirect to a specific page here you can just call Response.Redirect ("HandleError.aspx") or whatever your page may be. This method of handling errors is good for several reasons. 1.	If you need to override the Application_Error or the customErrors setup in the web.config file 2.	If each page must implement it's own error handling If you need to log specific information and then carry on, just code for your logging or whatever here, and that is all. If you need to cancel the error processing here (so it doesn't go to the Application_Error or customErrors) simply call Server.ClearError in this sub. Using the global.asax File The global.asax file contains the next line of defense against errors. When an error occurs, the Application_Error sub is called. This location happens to be my favorite place to log errors because it is the most functional. For most of my applications in .NET, I don't handle too many custom errors at the page level. I handle them at the application level. The only two locations that actually give you access to Server.GetLastError are in the Page_Error and Application_Error subs. After the Page_Error is called, the Application_Error sub is called. Here you can also log the error and redirect to another page. I won't explain anything else about it because it is basically the same as the Page_Error but happens to be at the application level rather than the page level. Using the web.config File The customErrors element of the web.config file is the last line of defense against an unhandled error. If you have other error handlers in place, like the Application_Error of Page_Error subs, these will get called first. Provided they don't do a Response.Redirect or a Server.ClearError, you should be brought to the page(s) defined in the web.config. In the web.config file, you can handle specific error codes (500, 404, etc), or you can use one page to handle all errors. This is a major difference between this method and the others (although you can emulate this by doing various Response.Redirects using the other methods). Open up your web.config file. The customErrors section uses this format:

  

Here is some important information about the "mode" attribute: "On" specifies that custom errors are enabled. If no defaultRedirect is specified, users see a generic error. "Off" specifies that custom errors are disabled. This allows display of detailed errors. "RemoteOnly" specifies that custom errors are shown only to remote clients, and ASP.NET errors are shown to the local host. This is the default. By default, the section looks like this when you create a Web application. 

This will show a generic page to users. To redirect it to one of your own pages, you would change it to this:



Now all errors that occur will be brought to the error.htm page. To handle specific errors, and redirect to the error page for everything else you can specify the error code you want specially handled like so:     

There is a problem here with this solution. Once the redirect is done, your error information is no longer available on the redirected page. This is because IIS (via the .net framework) performs a plain old GET request to the error page and does not do a "Server.Transfer" like the built-in IIS error handling does. The only information available to you at this time is the URL that caused this error to be raised. This is located on the querystring as "aspxerrorpath": http://localhost/ErrorHandling/error500.aspx?aspxerrorpath=/ErrorHandling/WebForm1.aspx. The only places this information is available is the two methods described above. Another interesting point about the above customErrors element is that you can specify different error pages for different subdirectories. For this example, let's say you have a directory named "Customers" off of your root directory that contains a branding specific for logged in customers but is not in its own application. As such you want to define a different set of pages for errors. Please note that these pages specified in the "redirect" attribute are relative to the "Customers" subdirectory and not the root path of your site. I have also included a security rule which says only MYDOMAIN\Customers can access these files. You can define rules for these errors in the web.config file:

 ...     ...   

    <error statusCode="401" redirect="CustomerAccessDenied.aspx"/> <error statusCode="404" redirect="CustomerPageNotFound.htm"/> <error statusCode="403" redirect="noaccessallowed.htm"/> </customErrors> <allow roles="MYDOMAIN\Customers" /> <deny users="*" /> </system.web>

Note: One thing I found in development is there seems to be an inheritance order for these errors. What I mean by this is if you have a 500 error defined for the root site, but none defined for the customers directory, but you DO have a defaultRedirect set for the customer directory, the 500 handler defined at the root level will be called. So if a parent directory has a handler.