1. Right-click the project entry in Solution Explorer and then select Add New Item.
2. Choose the Global Application Class template and click Add.
3. Modify Application_Error like this:
void Application_Error(Object sender, EventArgs e) {
// Log all unhandled errors
Utilities.LogError(Server.GetLastError());
}
4. In Solution Explorer, double-click web.config and add the following element as a child of the
<system.web> element:
<customErrors mode="RemoteOnly" defaultRedirect="Oooops.aspx" />
■Note After this change, remote clients will be forwarded to Oooops.aspx when unhandled exceptions are thrown; however, on the local machine, you’ll still receive detailed error information. If you want to see the same error message as your visitors, set mode to On instead of RemoteOnly.
5. Add a new Web Form to your application’s root, named Oooops.aspx, based on BalloonShop.master. 6. While in Source View, modify the page by changing its title and adding content to its content placeholder:
<%@ Page Language="C#" MasterPageFile="~/BalloonShop.master"
AutoEventWireup="true" CodeFile="Oooops.aspx.cs" Inherits="Oooops"
Title="BalloonShop - Oooops!" %>
<asp:Content ID="Content1" ContentPlaceHolderID="contentPlaceHolder"
runat="Server">
<p align="center">
<span class="CatalogTitle">Your request generated an internal error!
</span>
<br />
<br />
<span class="CatalogDescription">We apologize for the inconvenience!
The error has been reported. </span>
</p>
</asp:Content>
How It Works: Error Reporting
Right now your web site, no matter what happens, will look good. In case of an error, instead of displaying the default error message (which is, of course, not colorful enough for our customers’ tastes), it will display a nice- looking error message page. For a short test, configure your application to show the error page to you as well, not only to your visitors (there’s a note about how to do this in the exercise). Remember to unset this option, however, because you need exception details when building and debugging the application.
Then, execute the project, click on a department, and add a few letters to the department ID, like this:
http://localhost/BalloonShop/Catalog.aspx?DepartmentID=1ABC
Trying to load this page generates an exception because the department ID is supposed to be numerical, and the business tier code tries to convert this to an integer before sending it to the database (this is a good technique to prevent sending bogus values to the database).
Have a look at the custom error page in Figure 3-22.
Figure 3-22. Oooops!
First, this exception is caught in the data tier, which simply reports it and then rethrows it. The report from the data tier generates an email with contents like this:
Exception generated on Sunday, September 25, 2005, at 1:15 AM Page location: /BalloonShop/Catalog.aspx?DepartmentID=1ABC
Message: Failed to convert parameter value from a String to a Int32.
Source: System.Data
Method: System.Object CoerceValue(System.Object, System.Data.SqlClient .MetaType)
Stack Trace:
at System.Data.SqlClient.SqlParameter.CoerceValue(Object value, MetaType destinationType)
at System.Data.SqlClient.SqlParameter.GetCoercedValue() at System.Data.SqlClient.SqlParameter.Validate(Int32 index)
at System.Data.SqlClient.SqlCommand.SetUpRPCParameters(_SqlRPC rpc, Int32 startCount, Boolean inSchema, SqlParameterCollection parameters)
at System.Data.SqlClient.SqlCommand.BuildRPC(Boolean inSchema, SqlParameterCollection parameters, _SqlRPC& rpc)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) at System.Data.SqlClient.SqlCommandExecuteReader(CommandBehavior behavior, String method)
at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.ExecuteReader()
at GenericDataAccess.ExecuteSelectCommand(DbCommand command) in c:\Inetpub\wwwroot\BalloonShop\App_Code\GenericDataAccess.cs:line 31
This email contains all the significant details about the error. However, the error is rethrown, and because it isn’t handled anywhere else, it’s finally caught by the presentation tier, which displays the nice error page. A second email is generated, which should be taken seriously because the error caused the visitor to see an error message:
Exception generated on Sunday, September 25, 2005, at 1:15 AM Page location: /BalloonShop/Catalog.aspx?DepartmentID=1ABC
Message: Exception of type 'System.Web.HttpUnhandledException' was thrown.
Source: System.Web
Method: Boolean HandleError(System.Exception) Stack Trace:
at System.Web.UI.Page.HandleError(Exception e) at System.Web.UI.Page.ProcessRequestMain(Boolean
includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest()
at System.Web.UI.Page.ProcessRequest(HttpContext context) at
System.Web.HttpApplication.CallHandlerExecutionStep.System.
Web.HttpApplication.IexecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep
(IExecutionStep step, Boolean& completedSynchronously)