ASP.NET.in.a.Nutshell.Second.Edition [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

ASP.NET.in.a.Nutshell.Second.Edition [Electronic resources] - نسخه متنی

G. andrew Duthie; matthew Macdonald

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید










10.1 Error Handling


The
goal of error handling (also known as exception handling) is quite
simple: to prevent exceptions or errors thrown during the execution
of an application request from reaching users. Ideally, users should
not know that an exception occurred, or they should at least be
provided with an informative message that tells them what they can do
to resolve the problem. ASP.NET provides three techniques for
achieving this goal:

Custom

error

pages



Allow you to assign one or more error pages to be displayed when an
exception occurs.


Page_Error and Application_Error events



Writing event handlers for either or both of these events allows you
to catch and handle exceptions at the page or application level.


Structured

exception

handling



New to Visual Basic .NET, and also available in C#, this type of
exception handling allows exceptions to be caught and handled in
particular blocks of code.



These three techniques provide broadest (custom error pages, which
can handle exceptions from any page in the application) to narrowest
(structured exception handling, which handles exceptions for a
specific block of code) coverage for handling application exceptions.
Figure 10-1 illustrates the relationship of these
exception handling techniques to both the exception (shown at the
center) and the user, who you're trying to prevent
from encountering the exception.


Figure 10-1. Exception handling techniques


The following sections describe these techniques and explain how they
fit into an ASP.NET application. Note that you can use all three
techniques together, individually, or in whatever combination you
like. Using all three techniques in combination would provide broad
coverage for most exceptions and more robust specific exceptions
handling, but at the cost of maintaining your exception-handling
logic in more places.


10.1.1 Custom Error Pages


The most general, but arguably the simplest, technique for handling
exceptions in ASP.NET applications is to implement one or more custom
error pages. You can do this by creating a web page to display an
error message to the user. Then you specify that page as the default
error page (or to handle a specific class of error) in

web.config , using the
<customError> configuration element.
Example 10-1 shows a

web.config
file that defines a default custom error page called

Error.aspx . Example 10-2 shows the custom error page itself, which
simply displays the path of the page on which the error occurred.
Example 10-3 shows the code for a page that will
generate a NullReferenceException (which has an HTTP status code of
500).

Example 10-1. Enabling custom errors in web.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<customErrors defaultRedirect="Error.aspx" mode="On" />
</system.web>
</configuration>

Example 10-2. Error.aspx

<%@ Page Language="VB" %>
<html>
<head>
<title>Error page</title>
</head>
<body>
<h1>Error page</h1>
Error originated on: <%=Request.QueryString("aspxerrorpath") %>
</body>
</html>

Example 10-3. Throw500.aspx

<%@ Page Language="VB" Debug="True" %>
<html>
<head>
<title>Throw an Error</title>
<script runat="server">
Sub Page_Load( )
Dim NullText As String = Nothing
Message.Text = NullText.ToString( )
End Sub
</script>
</head>
<body>
<asp:label id="Message" runat="server"/>
</body>
</html>

Instead of the On mode setting for the
<customErrors> element, you can set the mode
to RemoteOnly or Off (note that
these values are case-sensitive). Off will cause
detailed error messages containing information about an unhandled
exception to be returned to the client, regardless of whether the
request is local or remote. Since you don't want
users to see error messages if you can avoid it,
it's best not to use this value in a production
application. RemoteOnly (the default) displays
detailed error messages for requests originating from the local host,
but displays custom errors (based on the
<customErrors> section) to remote clients.
On displays the custom error page(s) you specify
to any client, regardless of whether the request is local or remote.
Using RemoteOnly is a good practice for production
applications, since it prevents potentially sensitive information or
source code from being displayed to clients, while allowing
administrators or developers to view the page locally to read this
information.

In addition to providing a default error page, the
<customErrors> element also supports the
use of child
<error> elements to specify custom
error pages for specific classes of errors, such as authentication
(HTTP 403) or Not Found (HTTP 404) errors, as shown in the following
code snippet:

<customErrors defaultRedirect="Error.aspx" mode="On">
<error statusCode="403" redirect="ErrorAccessDenied.aspx"/>
<error statusCode="404" redirect="ErrorNotFound.aspx"/>
</customErrors>

Any errors for which there is not a specific
<error> element defined are handled by the
page specified by the defaultRedirect
attribute. Having different error pages for specific errors allows
you to provide more informative messages to users, and perhaps offer
some instructions on how the errors can be remedied, while still
providing a generic handler for errors outside the scope of the
specified handlers.

Another important thing you can do in a custom error page, whether
specific or generic, is provide logging or notification of the error
so that the site developer or administrator knows that there is a
problem and can take action to fix it. In ASP.NET, this process is
fairly simple and can be accomplished through the use of the
MailMessage and
SmtpMail classes, which
reside in the System.Web.Mail namespace. Example 10-4
shows a custom error page that uses these classes to notify a site
administrator of the error and the page on which it occurred.

Example 10-4. Error_SendMail.aspx

<%@ Page Language="VB" %>
<%@ Import Namespace="System.Web.Mail" %>
<html>
<head>
<title>Error page</title>
<script runat="server">
Sub Page_Load( )
Dim Mail as New MailMessage( )
'Change the values below to valid email addresses
Mail.To = "<valid email address>"
Mail.From = "<valid email address>"
Mail.Subject = "aspnetian.com error"
Mail.Body = "An Exception occurred in page " & _
Request.QueryString("aspxerrorpath")
'If your SMTP server is not local, change the property below
' to a valid server or domain name for the SMTP server
SmtpMail.SmtpServer = "localhost"
SmtpMail.Send(Mail)
End Sub
</script>
</head>
<body>
<h1>Error page</h1>
Error originated on: <%=Request.QueryString("aspxerrorpath") %>
<br/>
An email has been sent to the administrator of this site notifying them of the error.
</body>
</html>

For the code in Example 10-4 to work, you need to
provide valid email addresses for the To and From properties of the
MailMessage object instance.

If an unhandled exception occurs in a custom error page, no further
redirect will occur, so the user will see a blank page. This
situation makes it extremely important for you to ensure that no
unhandled exceptions occur in custom error pages. For example, it
might be a good idea to wrap the call to

SmtpMail.Send in Example 10-4 in
a Try...Catch block) to handle potential problems
with connecting to the specified SMTP server. For more information
about using the Try...Catch block, see Section 10.1.3, later in this chapter.

The advantage of using custom error pages is that it allows you to
handle a lot of errors from a single location (or a small number of
locations). The disadvantage is that there's not
much you can do to handle the error, other than display a helpful
message and notify someone that an error occurred. The reason for
this is that you don't have access to the actual
exception object in a custom error page, which means you can neither
display information about the specific exception nor take steps
to handle
it.


10.1.2 Page_Error and Application_Error


Another technique for error handling that provides
the ability to handle a broad range of application errors is the use
of the Error event defined by the Page and
HttpApplication
classes. Unless AutoEventWireup has been set to
False (the default in Web Forms pages created with
Visual Studio .NET), ASP.NET automatically calls page or
application-level handlers with the name Page_Error or
Application_Error if an unhandled exception occurs at the page or
application level, respectively. The handler for Page_Error should be
defined at the page level, as shown in Example 10-5,
while the handler for Application_Error should be defined in the
application's

global.asax file, as
shown in Example 10-6.

Example 10-5. Throw500_Page_Error.aspx

<%@ Page Language="VB" %>
<%@ Import Namespace="System.Web.Mail" %>
<html>
<head>
<title>Throw an Error</title>
<script runat="server">
Sub Page_Load( )
Dim NullText As String = Nothing
Message.Text = NullText.ToString( )
End Sub
Sub Page_Error(Source As Object, E As EventArgs)
Dim ex As Exception = Server.GetLastError( )
If Not ex Is Nothing Then
Dim Mail as New MailMessage( )
'Change the values below to valid email addresses
Mail.To = "<valid email address>"
Mail.From = "<valid email address>"
Mail.Subject = "aspnetian.com error"
Mail.Body = "An Exception occurred in page " & _
Request.RawUrl & ":" & vbCrLf
Mail.Body &= ex.ToString( ) & vbCrlf & vbCrlf
Mail.Body &= "was handled from Page_Error."
'If your SMTP server is not local, change the property below
' to a valid server or domain name for the SMTP server
SmtpMail.SmtpServer = "localhost"
SmtpMail.Send(Mail)
Server.ClearError( )
End If
Response.Write("An error has occurred. " & _
"The site administrator has been notified.<br/>" & _
"Please try your request again later.")
End Sub
</script>
</head>
<body>
<asp:label id="Message" runat="server"/>
</body>
</html>

Example 10-5 deliberately causes a
NullReferenceException exception by calling ToString on an object
that is set to Nothing. In Page_Error, we retrieve
this exception by calling Server.GetLastError. The example then
creates and sends an email that includes the exception details
(calling ToString on an exception object returns the error message
and the call stack as a string).

Finally, the code clears the exception by calling Server.ClearError.
This last step is important because neither the Page_Error nor the
Application_Error handler clears the exception by default. If you
don't call ClearError, the exception will bubble up
to the next level of handling. For example, if you define both a
Page_Error handler at the page level and an Application_Error handler
in

global.asax , and you do not call ClearError
in Page_Error, the Application_Error handler is invoked in addition
to Page_ Error. This can be a useful behavior if expectedfor
example, if you wish to use Page_Error to generate useful messages,
while using Application_ Error to log all errors or send
notifications. If you're not expecting it, though,
this behavior can be confusing, to say the least.

Example 10-6 does essentially the same thing as Example 10-5, but handles errors at the application level,
rather than at the page level. You can still access the Server object
to get the exception that was thrown. Since Application_Error may
handle exceptions for web services as well as for Web Forms pages,
Example 10-6 does not attempt to use Response.Write
to send a message to the user.

Example 10-6. global.asax

<%@ Import Namespace="System.Web.Mail" %>
<script language="VB" runat="server">
Sub Application_Error(sender As Object, e As EventArgs)
Dim ex As Exception = Server.GetLastError( )
If Not ex Is Nothing Then
Dim Mail as New MailMessage( )
'Change the values below to valid email addresses
Mail.To = <valid email address>
Mail.From = <valid email address>
Mail.Subject = "aspnetian.com error"
Mail.Body = "An Exception occurred in page " & _
Request.RawUrl & ":" & vbCrLf
Mail.Body &= ex.ToString( ) & vbCrlf & vbCrlf
Mail.Body &= "was handled from Application_Error."
'If your SMTP server is not local, change the property below
' to a valid server or domain name for the SMTP server
SmtpMail.SmtpServer = "localhost"
SmtpMail.Send(Mail)
Server.ClearError( )
End If
End Sub
</script>


10.1.3 Structured Exception Handling


The most specific technique for exception
handling, and the most useful in terms of gracefully recovering from
the exception, is structured exception handling. Structured exception
handling should be familiar to developers of Java and C++, for which
it is standard practice, but it is new to the Visual Basic .NET
language. Microsoft's new language, C#, also
provides built-in support for structured exception handling.

In structured exception handling, you wrap code that may throw an
exception in a Try...Catch block, as shown in the
following code snippet:

'VB.NET
Try
' Code that may cause an exception
Catch ex As Exception
' Exception handling code
Finally
' Code executes whether or not an exception occurs
End Try
//C#
try
{
// Code that may cause an exception
}
catch (Exception ex)
{
// Exception handling code
}
finally
{
// Code executes whether or not an exception occurs

The Try statement
(lowercase try in C#) warns the runtime that the
code contained within the Try block may cause an
exception; the Catch statement
(catch in C#) provides code to handle the
exception. You can provide more than one Catch
statement, with each handling a specific exception, as shown in the
following code snippet. Note that each exception to be handled must
be of a type derived from the base Exception
class:

'VB.NET
Try
' Code that may cause an exception
Catch nullRefEx As NullReferenceException
' Code to handle null reference exception
Catch ex As Exception
' Generic exception handling code
End Try
//C#
try
{
// Code that may cause an exception
}
catch (NullReferenceException nullRefEx)
{
// Code to handle null reference exception
}
catch (Exception ex)
{
// Generic exception handling code
}


When using multiple Catch blocks, the
blocks for specific exceptions should always appear before any
Catch block for generic exceptions, or the
specific exceptions will be caught by the generic exception handler.

The Finally statement
(finally in C#) is also useful in structured
exception handling. When used in conjunction with a
Try...Catch block, the Finally
statement allows you to specify code that will always be run
regardless of whether an exception is thrown. This can be especially
useful if you need to run clean-up code that might not otherwise run
if an exception occurred, such as code that closes a database
connection and/or rolls back a database transaction to avoid leaving
data in an inconsistent state. Example 10-7 shows a
page that attempts to connect to the Pubs SQL Server database and
execute a command that returns a SqlDataReader. If either the
connection attempt or the command results in an exception, the code
in the Catch block will be executed. The code in
the Finally block tests to see if the data reader
and/or connection are open. If they are, it closes them.

Example 10-7. ReadTitles.aspx

<%@ Page Language="VB" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<html>
<title>Try-Catch-Finally Example</title>
<head>
<script runat="server">
Sub Page_Load( )
Dim ConnStr As String = "Data Source=(local)\NetSDK;" & _
"Initial Catalog=Pubs;Trusted_Connection=True;"
Dim SQL As String = "SELECT title, price FROM title " & _
"WHERE PRICE IS NOT NULL"
Dim PubsConn As New SqlConnection(ConnStr)
Dim TitlesCmd As New SqlCommand(SQL, PubsConn)
Dim Titles As SqlDataReader
Try
PubsConn.Open( )
Titles = TitlesCmd.ExecuteReader( )
Output.Text = "<table>"
While Titles.Read( )
Output.Text &= "<tr>"
Output.Text &= "<td>" & Titles.GetString(0) & "</td>"
Output.Text &= "<td>$" & _
Format(Titles.GetDecimal(1), "##0.00") & "</td>"
Output.Text &= "</tr>"
End While
Output.Text &= "</table>"
Catch sqlEx As SqlException
Response.Write("A SqlException has occurred.")
Catch ex As Exception
Response.Write("An Exception has occurred.")
Finally
If Not Titles Is Nothing Then
If Not Titles.IsClosed Then
Titles.Close( )
End If
End If
If PubsConn.State = ConnectionState.Open Then
PubsConn.Close( )
End If
End Try
Response.Write("<br/>The current connection state is: " & _
PubsConn.State.ToString( ) & ".")
End Sub
</script>
</head>
<body>
<h1>SqlDataReader Example</h1>
<asp:label id="Output" runat="server"/>
</body>
</html>

As you can see from the examples in this section, of the available
exception-handling techniques, structured exception handling is
likely to require the most code to implement. However, it also
provides you with the ability to handle the exception transparently
to the user in cases when it is possible to recover from the
exception. For example, you could modify the code in Example 10-6 to test whether the exception was related to
the attempt to open the connection and, if so, retry the connection a
predefined number of times. This way, if the exception is the result
of a temporary network problem, the exception handling code can
potentially handle this problem without the user ever being aware
that a problem exists (apart from the slight
delay in connecting).


    / 873