Exception and Error Handling
When an exception occurs in your ASP.NET application code, you can handle it in a number of ways, but the best approach is a multi-pronged one:
? Catch what you expect:
? Use a Try/Catch around error-prone code. This can always catch specific exceptions that you can deal with, such as System.IO.FileNotFoundException
? Rather than catching exceptions around specific chunks of code at the page level, consider using the page-level error handler to catch specific exceptions that might happen anywhere on the page.
? But prepare for unhandled exceptions:
? Set the Page.Error property if a specific page should show a specific error page for any unhandled exception. This can also be done using the <%@ Page > directive or the code behind the property.
? Have default error pages for 400 and 500 errors set in your web.config.
? Have a boilerplate Application_OnError handler that takes into consideration both specific exceptions that you can do something about, as well as all unhandled exceptions that you may want logged to either the event log, a text file, or other instrumentation mechanism.
The phrase unhandled exception may be alarming, but remember that you don’t do anyone any good catching an exception that you can’t recover from. Unhandled exceptions are okay if they are just that — exceptional. For these situations, rely on global exception handlers for logging and friendly error pages that you can present to the user.
Why try to catch an exception by adding code everywhere if you can catch and log exceptions all in one place? A common mistake is creating a try/catch block around some arbitrary code and catching the least specific exception type — System. Exception. A rule of thumb is, don’t catch any exception that you can’t do anything about. Just because an exception can be thrown by a particular method doesn’t mean you have to catch it. It’s exceptional, remember? Also, there are exception handlers at both the page and the application level. Catch exceptions in these two centralized locations rather than all over.
Handling Exceptions on a Page
To handle exceptions at a page level, override the OnError method that System.Web.UI.Page inherits from the TemplateControl class (see Listing 24-5). Calling Server.GetLastError gives you access to the exception that just occurred. Be aware that a chain of exceptions may have occurred, and you can use the ExceptionGetBaseException method to return the root exception.
Page-level error handling
protected override void OnError(EventArgs e)
{
System.Exception anError = Server.GetLastError();
if (anError.GetBaseException() is SomeSpecificException)
{
Response.Write("Something bad happened!");
Response.StatusCode = 200;
Server.ClearError();
Response.End();
}
}
Handling Application Exceptions
The technique of catching exceptions in a centralized location can be applied to error handling at the application level in Global.asax, as shown in Listing 24-6. If an exception is not caught on the page, the web.config is checked for an alternate error page; if there isn’t one, the exception bubbles up to the application and your user sees a complete call stack.
Application-level error handling
protected void Application_Error(Object sender, EventArgs e)
{
System.Exception bigError = Server.GetLastError();
//Example checking for HttpRequestValidationException
if(bigError.GetBaseException() is HttpRequestValidationException )
{
System.Diagnostics.Trace.WriteLine(bigError.ToString());
Server.ClearError();
}
}
Unhandled application errors turn into HTTP Status Code 500 and display errors in the browser. These errors, including the complete callstack and other technical details, may be useful during development, but are hardly useful at production time. Most often, you want to create an error handler (as shown previously) to log your error and to give the user a friendlier page to view.
If you ever find yourself trying to catch exceptions of type System.Exception, take a look at the code to see whether you can avoid it. There’s almost never a reason to catch such a non-specific exception, and you’re more likely to swallow exceptions that can provide valuable debugging. Check the API documentation for the framework method you are calling — a section specifically lists what exceptions an API call might throw. Never rely on an exception occurring to get a standard code path to work.
Http Status Codes
Every HttpRequest results in an HttpResponse, and every HttpResponse includes a status code. The following table describes 11 particularly interesting HTTP status codes.
Status Code Explanation
200 OK Everything went well.
301 Moved Permanently
Reminds the caller to use a new, permanent URL rather than the one he used to get here.
302 Found Returned during a Response.Redirect. This is the way to say ‘‘No, no, look over here right now.’’
304 Not Modified
Returned as the result of a conditional GET when a requested document hasn’t been modified. It is the basis of all browser-based caching. An HTTP message-body must not be returned when using a 304.
307 Temporary Redirect
Redirects calls to ASMX Web services to alternate URLs. Rarely used with ASP.NET.
400 Bad Request
Request was malformed.
401 Unauthorized
Request requires authentication from the user.
403 Forbidden Authentication has failed, indicating that the server understood the requests but cannot fulfill it.
404 Not Found The server has not found an appropriate file or handler to handle this request. The implication is that this may be a temporary state. This happens in ASP.NET not only because a file cannot be found, but also because it may be inappropriately mapped to an IHttpHandler that was not available to service the request.
410 Gone The equivalent of a permanent 404 indicating to the client that it should delete any references to this link if possible. 404s usually indicate that the server does not know whether the condition is permanent.
500 Internal Server Error
The official text for this error is ‘‘The server encountered an unexpected condition which prevented it from fulfilling the request,’’ but this error can occur when any unhandled exception bubbles all the way up to the user from ASP.NET.
Any status code greater than or equal to 400 is considered an error and, unless you configure otherwise, the user will likely see an unfriendly message in his browser. If you have not already handled these errors inside of the ASP.NET runtime by checking their exception types, or if the error occurred outside of ASP.NET and you want to show the user a friendly message, you can assign pages to any status code within web.config, as the following example shows:
<customErrors mode ="On" >
<error statusCode ="500" redirect ="FriendlyMassiveError.aspx" />
</customErrors>
After making a change to the customer errors section of your web.config, make sure a page is available to be shown to the user. A classic mistake in error redirection is redirecting the user to a page that will cause an error, thereby getting him stuck in a loop. Use a great deal of care if you have complicated headers or footers in your application that might cause an error if they appear on an error page. Avoid hitting the database or performing any other backend operation that requires either user authorization or that the user’s session be in any specific state. In other words, make sure that the error page is a reliable standalone.
Any status code greater than or equal to 400 increments the ASP.NET Requests Failed performance counter. 401 increments Requests Failed and Requests Not Authorized. 404 and 414 increment both Requests Failed and Requests Not Found. Requests that result in a 500 status code increment Requests Failed and Requests Timed Out. If you’re going to return status codes, you must realize their effects and their implications.
Filed under:
.NET,
ASP.NET,
Code,
Design,
Reference,
Web Tagged:
.NET4,
ASP.NET,
Attacks,
Auditing,
Books,
Errors,
Exceptions,
Logging Blocks,
MS,
Practices,
Security,
Tips,
Tracing