To be able to handle errors gracefully, it is imperative that some mechanism exists to inform us developers when an error arises. Errors occurring in an ASP page can be detected in one of two ways: through the scripting language’s error-handling interface, or through ASP 3.0’s new ASPError object.
The VBScript and JScript 5.0 scripting engines both contain robust runtime error-detection mechanisms. ASP pages (or other scripts) created using either of these scripting languages can use the scripting language’s inherent error-handling capabilities to detect when errors occur. As we’ll see in Section 3.2.1, using either VBScript’s or JScript’s error-handling mechanisms often results in verbose, inelegant, and unreadable code when compared to the elegance of the ASPError object.
The ASPError object, which we’ll detail in Section 3.2.2, provides detailed information about an error that just occurred in an ASP page, including a description and line number. However, the ASPError object can only be used with IIS 5.0/ASP 3.0, which, of course, requires Windows 2000 as the web server. Therefore, if you are using Windows NT as your web server’s operating system, you must rely on the scripting languages’ error-handling techniques to detect and gracefully respond to exceptions.
If you are using Windows 2000, though, you are in luck. To use the ASPError object, a separate error-handling web page is needed. This separate page is responsible for receiving the detailed error information and providing an understandable error message to the end user. Using this approach, an error that occurs on any line in any ASP page will result in a call to the error-handling web page. Furthermore, the ASPError object provides more detailed information on the error that occurred than either of the VBScript or JScript error-handling objects.
Using the ASPError object approach alone makes it next to impossible
to detect an error, circumvent it, and continue processing. Once an
error-handling web page is set up, when an error occurs, the user is
sent to the error-handling web page via a
Server.Transfer
. Therefore, you can’t fix
the error on the error-handling web page and resume processing on the
ASP page where the error originated. We’ll look at the ASPError
object in finer detail later in this chapter in Section 3.2.2.
Both the VBScript and JScript 5.0 scripting engines provide runtime
error-handling routines to assist in detecting and gracefully
handling implementation errors. VBScript’s error handling is
very similar to Visual Basic’s, using the On
Error Resume
Next
notation and
the Err object. JScript’s error handling is similar to
C++’s and Java’s, using try
...
catch
blocks. Regardless of the scripting language
used, providing adequate error handling usually results in bloating
one’s code severely.
If your web site is not running on IIS 5.0, then unfortunately, you do not have access to the ASPError object, and therefore must rely on the techniques discussed in this section to detect errors. If, however, you are using IIS 5.0, and all you plan on doing when an error occurs is displaying a friendly error message to the end user, then it is highly recommended that you rely on the error handling provided by the new ASP 3.0 ASPError object.
The main advantage the scripting language error-handling techniques have over the ASPError object is the ability to recover from an error. Using these techniques, a developer can detect when an error occurs and decide on some alternative course of action, keeping the script running smoothly from the perspective of the end user.
VBScript and JScript error-handling mechanisms can only detect runtime errors. You cannot use these techniques to detect, recover from, or display readable error messages about compile-time errors.
If you are familiar with error handling in Visual Basic, you’ll find VBScript error handling easy to pick up. By default, error handling is disabled. This means if an error occurs during the execution of a VBScript script, the script is halted and the user is presented with an error message. Usually these error messages are quite cryptic and daunting for the end user. Imagine a visitor to your web site who is presented with an error similar to:
Server object error 'ASP 0177 : 800401f3' Server.CreateObject Failed /SomePage.asp, line 14 Invalid class string
Would you expect this customer to return to your site in the future? This daunting error message could be modified to a more friendly error message that is less likely to scare the user away. To be able to determine when an error occurs in a script, though, error handling must be enabled.
In VBScript, error handling is enabled and disabled using the
On
Error
statement. To enable
error handling, enter the following
command at the top of your ASP page:
On Error Resume Next
This informs the scripting engine that when an error occurs, it should proceed to the next line of code rather than abruptly stopping and displaying an error message. To disable error handling, use the following statement:
On Error Goto 0
Simply enabling error handling is not enough to detect when an error
occurs, however. After each statement that might cause an error, an
If
statement needs to be used to determine whether
or not an error did occur. This technique is known as
inline error handling. I find this constant inquiring whether or not an error has occurred to be rather verbose and annoying, and liken it to an inefficient manager.
Imagine that you are a manager for one employee. This employee performs a number of tasks, and as a manager, it is your job to ensure that these tasks do not cause any errors. After each task this employee performs that might cause an error, you take a moment out of your schedule to ask the employee whether or not an error occurred. Such a model is far from efficient and is very verbose, containing many unneeded communications between manager and employee. Ideally, the employee would know when an error occurs, and would be able to approach you, the manager, when something has gone awry. However, with VBScript and JScript error handling, we do not have this option, and must continually be bothered with having to inquire whether or not an error has occurred.
How often should you, the manager, ask your employee if an error has occurred? Ideally, after every statement that might generate an error, the employee should be queried if an error occurred. Of course, what statements might generate an error? Deciding when to check for an error is not always easy. This topic is discussed in greater detail in Section 3.2.1.2, next.
Example 3.1 contains an example of error handling.
Note that at the start of the script, On
Error
Resume
Next
is issued, enabling error handling. Then,
after each statement that might cause an error, a conditional
statement is used to determine whether or not an error has occurred.
If an error has occurred, a short error message is presented to the
visitor.
Example 3-1. Using VBScript to Detect an Error
<%@ LANGUAGE = "VBSCRIPT" %> <% Option Explicit %> <% 'Enable error handling On Error Resume Next 'Create two objects Dim objConn, objRS Set objConn = Server.CreateObject("ADODB.Connection") 'Check to see if an error occurred If Err.Number <> 0 then Response.Write "Error in creating Connection object.<BR>" Response.Write "Description: " & Err.Description Response.End End If ' The ProgID is invalid, so this line should generate an error Set objRS = Server.CreateObject("Database Recordset") 'Check to see if an error occurred If Err.Number <> 0 then Response.Write "Error in creating Recordset object.<BR>" Response.Write "Description: " & Err.Description Response.End End If 'If we've reached here, no errors! Set objRS = Nothing Set objConn = Nothing %>
VBScript provides an Err object to handle runtime errors. When error handling is enabled and an error occurs, the Err object’s five properties are automatically populated with the error information from the latest error. These properties are presented in Table 3.1.
Table 3-1. The Err Object’s Properties
Property |
Description |
---|---|
Number |
A numeric value or code uniquely identifying the error |
Source |
The name of the application or object that generated the error |
Description |
A lengthy description of the error |
HelpFile |
A fully qualified path to a help file |
HelpContext |
A context ID for a topic in a help file |
To determine whether an error has just occurred, check the Number property of the Err object. If no error has occurred, this property will equal zero. If an error has occurred, the property’s value will be unequal to (i.e., either greater than or less than) zero.
The
Err object
contains two methods: Clear and Raise. The Clear method simply resets
all of the property values. If you plan on trying to recover from an
error, you will need to use the Clear method once you detect an error
and find some alternative path around the error. If the Clear method
is not used, the next time we check Err.Number
we
will find it is not equal to zero (since it still equals the value
from our first error). Therefore, if you plan on continuing execution
after handling an error, be sure to issue an
Err.Clear
after recovering from each error. The
following code snippet presents a case when you’d use the Clear
method:
If Err.Number =SomeSpecificErrorNumber
then 'Some specific error occurred, circumvent the problem somehowProblem Circumvented Somehow...
'Now that we took some alternative approach, we want to clear the 'Err objectErr.Clear
'Cleared the error... continue on with the script End If
The Raise
method generates a runtime error, and
has the following definition:
Err.Raise
Number
,Source
,Description
,HelpFile
,HelpContext
When developing reusable scripts, often the developer who writes a given reusable script is not the only one who uses it in practice. For example, in Chapter 5, we will look at how to create a reusable server-side form-validation script. While I wrote the code and use it, I don’t expect to be the only developer who will use it. The server-side form-validation script presented in Chapter 5 expects certain inputs from the developer using the script. If these inputs are out of acceptable ranges, an error is raised so the developer is notified immediately when trying to execute the script.
When raising errors in your own objects, Microsoft suggests that you
add the VBScript
constant
vbObjectError
to the custom error code. Examples
in this book using the Raise method add
vbObjectError
to the error number, like so:
Err.RaisevbObjectError
+ErrorNumber
, ...
There is nothing magical about
vbObjectError
. It is simply a constant with a value
of 80040000
hexadecimal.
Since this book focuses on building reusable, robust scripts, throughout this book we’ll use the Raise method of the Err object extensively.
Virtually any line of code can cause an exception. A given exception is either consistent or inconsistent. A consistent exception is one that occurs every time the script is executed. For example, the following line of code is an example of a consistent exception:
Dim 4GuysFromRolla 'variable names cannot begin with a number
This exception is consistent, since every single time an ASP page is visited with the above line of code, an error will be generated. The following lines of code demonstrate an inconsistent exception:
'Connect to a database Dim objConn Set objConn = Server.CreateObject("ADODB.Connection") objConn.ConnectionString = "DRIVER={Microsoft Access Driver (*.mdb)};" & _ "DBQ=C:InetPubwwwrootdatastoresNWind.mdb;" & _ "UID=admin;PWD=;" objConn.Open 'Open a connection to the database
An error would occur if the database file
NWind.mdb
did not exist in the path specified in
the ConnectionString property
(C:InetPubwwwrootdatastores
). Such an
exception is inconsistent because the above script would work
flawlessly so long as the database was in the right directory.
Imagine, though, that NWind.mdb
was moved or
renamed. Once that occurred, the script would start producing error
messages when the Open method was reached.
Consistent exceptions are simple to detect. When testing a script that contains:
Dim 4GuysFromRolla
a developer can quickly diagnose and remedy the problem. Since
inconsistent exceptions happen more unexpectedly, it is these
exceptions that error detection should be employed for. After each
line that may generate an inconsistent exception, be sure to place an
If
statement to check if
Err.Number
is not equal to zero. Those statements
that might cause inconsistent exceptions are usually limited to
methods of COM objects, such as the Open method of
the ADO Connection object. Therefore, when using COM objects in your
ASP scripts, be sure to check for errors immediately after calling
one of the COM object methods.
In Section 3.3, we look at how to gracefully handle exceptions that occur using VBScript exception handling.
If your ASP page contains any functions or subroutines, error handling becomes somewhat trickier. If you enable error handling at the start of your ASP page and then call a function or subroutine that contains a runtime error, the function or procedure will halt and pass control back through the call stack until a procedure with enabled error handling is found. Figure 3.1 illustrates this concept graphically.
Figure 3-1. When an error occurs in a non-error handling-enabled procedure, control is passed back through the call stack until a procedure with error handling enabled is found
When a function or subroutine is called, error handling is
deactivated. Therefore, if you want a function or subroutine to be
able to perform error handling on its own (that is, if you
don’t want it to halt and pass control back to the calling
procedure), you must explicitly turn on error handling in the
function or subroutine with
On
Error
Resume
Next
. Example 3.2 examines VBScript’s error-handling
conventions with functions and subroutines.
Example 3-2. When an Error Occurs in a Function or Subroutine, Control Is Passed Up the Call Stack Until an Error-Handling Enabled Function or Subroutine Is Found
<% @LANGUAGE="VBSCRIPT" %> <% Option Explicit %> <% 'Enable error handling On Error Resume Next 'Call Procedure1 Procedure1 'We can check to see if an error occurred If Err.Number <> 0 then Response.Write "An error occurred!<BR>" Response.Write Err.Number & " - " & Err.Description End If Sub Procedure1( ) 'No error handling here 'Call Procedure2 Procedure2 End Sub Sub Procedure2( ) 'No error handling here either 'Whoops, an error occurred! (Using an undeclared variable) strScott = strMitchell 'The above error will halt Procedure2, returning control to Procedure1. Since there is no error handling implemented there, ontrol is returned to the'line 'immediately following the call 'to Procedure1. Since error handling is enabled 'there, it will handle the error. (Otherwise, the user would be shown 'an error message.) End Sub %>
Note that the error-handling code after the call to
Procedure1
is tripped for the error that
occurred in Procedure2
. The output of the code
in Example 3.2 is as expected:
An error occurred! 500 - Variable is undefined
With VBScript error handling, after each statement that might cause an exception, the developer needs to determine whether an error occurred. The following code is usually found after every potential error-causing statement:
If Err.Number <> 0 then
Display an error message
End If
This approach can be streamlined a bit by using a
subroutine to determine if an error
has occurred or not. This subroutine,
CheckForError
, should have the following
definition:
Sub CheckForError(strErrorMessage
)
strErrorMessage
is a string containing the
error message that should be displayed to the user if an error
occurs. After each line of code that may cause an error, the clunky:
If Err.Number <> 0 then
Display an error message
End If
can be replaced with:
CheckForError strErrorMessage
The CheckForError
subroutine contains fairly
straightforward code, simply checking to see if
Err.Number
is nonzero. If it is,
strErrorMessage
is displayed and execution
is halted with Response.End
. Example 3.3 contains the source code for
CheckForError
.
Example 3-3. The CheckForError Subroutine Allows for More Streamlined Error-Handling Code in VBScript
Sub CheckForError(strErrorMessage) If Err.Number <> 0 then 'An error occurred! Display the error message Response.Write strErrorMessage Response.End End If End Sub
Unfortunately, this subroutine needs to exist in all of your ASP pages that use scripting-language error handling. Of course, server-side includes should be used so that only one instance of this function is needed. Later in Section 3.3, we look at how to improve this subroutine to include the ability to notify the support team of the exception’s details.
A list of VBScript runtime error numbers can be found on Microsoft’s scripting site at: http://msdn.microsoft.com/scripting/default.htm?/scripting/vbscript/doc/vsmscRunTimeErrors.htm. VBScript syntax error numbers are listed at: http://msdn.microsoft.com/scripting/default.htm?/scripting/vbscript/doc/vsmscSyntaxErrors.htm.
JScript’s error handling is similar to C++’s and
Java’s, using try
...
catch
blocks. Error handling is fairly new to
JScript; it’s only available in the JScript scripting engine
Version 5.0 or greater. To download the free, latest version of the
JScript and VBScript scripting engines, direct your browser
to http://msdn.microsoft.com/scripting.
If you have IIS 5.0 installed as your web server, the Version 5.0 scripting engines are already installed on your computer. If you are using IIS 4.0, you may have an older scripting engine version, and should upgrade your scripting engine at Microsoft’s Scripting Site (http://msdn.microsoft.com/scripting ).
In VBScript, error handling is enabled with the On
Error
Resume
Next
command. If an exception is raised, the Err
object is populated with the error information. To detect an error,
after each line of code that may be guilty of causing an error, a
test is performed, checking to see if Err.Number
is nonzero. In Section 3.2.1.1,these actions were likened to an
inefficient manager, continually checking on an employee to see if he
had performed some erroneous task.
JScript’s error handling is a bit different. In
JScript,
there is no single command (like VBScript’s On Error Resume Next
) that indicates that error handling should be
enabled. Furthermore, JScript’s error handling takes a more
direct approach than VBScript’s. To relate it to the
manager/employee analogy used earlier, rather than asking our
employee if an error occurred after he has attempted a task, we
cautiously tell our employee to try a certain set of tasks. The
employee then attempts to perform these tasks, and gets back to us if
any of these tasks caused an error.
To let our dutiful employee know what tasks to perform, a
try
block is used,
which has the following syntax:
try
{possibly erroneous statements
}
A try
block must be immediately followed by a
catch
block. A catch
block
contains code that is executed only if an error occurred in the
commands within the preceding try
block.
catch
(errorObjectInstance
) {error handling code
}
If an error occurs in one of the commands issued within a
try
block, the details of the error are
encapsulated within an Error object. An instance of this object is
assigned to the variable specified by
errorObjectInstance
immediately following
the catch
statement in the
catch
block.
The JScript Error object is similar to VBScript’s Err object, but only contains two properties and no methods. The two properties of the Error object can be seen in Table 3.2.
Table 3-2. The JScript Error Object Contains Two Properties
Property |
Description |
---|---|
description |
A string containing detailed information on the exception. |
Number |
A 32-bit number containing both a facility code and the actual error number. The upper 16 bits are the facility code, while the lower 16 bits are the error number. |
The way Microsoft chose to represent their error numbers is, in my opinion, a little confusing. The easiest way to pick apart an error number is to look at it in hexadecimal format. All error codes you’ll receive through an ASP page will have the following hexadecimal format:
800FNNNN
The F
denotes the facility code, which
indicates who raised the error. VBScript and JScript use a facility
code of 10 (A
, in hex). The
NNNN
represents the actual error number.
Therefore, using JScript’s number property, to get the
NNNN
portion of the 32-bit error number,
use the following:
try {some code...
} catch (err) { // display the error number, less the 800F
Response.Write("The error generated the following error code: "); Response.Write(
err.number & 0xFFFF
); }
Likewise, to obtain just the facility code, use the following:
try {some code...
} catch (err) { // display the error number, less the 800F
Response.Write("The error generated the following facility code: "); Response.Write(
err.number >> 16 & 0xF
); }
A list of JScript runtime error numbers can be found on Microsoft’s scripting site at http://msdn.microsoft.com/scripting/default.htm?/scripting/jscript/doc/jsmscRunTimeErrors.htm. JScript syntax error numbers are listed at http://msdn.microsoft.com/scripting/default.htm?/scripting/jscript/doc/jsmscSyntaxErrors.htm.
The try
block can contain several lines of code;
as soon as an error occurs, the corresponding
catch
block is visited. Therefore, a separate set
of try
... catch
blocks should
be used for each statement that might generate an exception. As
discussed in Section 3.2.1.2, method calls to COM
objects are the usual suspects for inconsistent exceptions;
therefore, a try
... catch
block should exist for each COM object method call.
Recall that VBScript’s Err object contains a Raise method,
which, as its name implies, raises an error. JScript’s Error
object does not contain any such method; to raise an error in
JScript, use the
throw
statement. throw
has the
following syntax:
throw
errorObjectInstance
;
errorObjectInstance
needs to be an Error
object instance with its number and description properties already
set. Example 3.4 demonstrates how to use the
throw
statement.
Example 3-4. The throw Statement Raises an Error
<% @LANGUAGE="JSCRIPT" %> <% function ThrowError( ) { // throw an error... var err;err = new Error( );
// Create an Error object instanceerr.description = "Egad, an error!";
err.number = 0x800A1234;
throw err;
// throw the error (similar to Err.Raise) } // try calling the function ThrowError try {ThrowError( );
} catch (err) { // If we've reached here, an error occurred. Display the error info... Response.Write("An error occurred! Here are the following details:"); Response.Write("<P><UL>Description: " + err.description); Response.Write("<BR>Error Number:</B> " + (err.number & 0xFFFF)); Response.Write("<BR>Facility Code:</B> " + (err.number >> 16 & 0xF)); } %>
try
...
catch
blocks can be nested within one another. For
example, the following is perfectly legal syntax:
try { // outer try block try { // inner try block // something } catch (err) { // this catch block is only visited if an error occurs in the // code within the inner try block } } catch (err) { // this catch block is only visited if an error occurs in the // code within the outer try block }
While explicit try
... catch
block nesting isn’t often used, implicit try
... catch
blocks are quite common. Example 3.5 illustrates a pair of try
... catch
blocks nested implicitly. The inner
try
... catch
block is within
the function foobar
; the outer
try
... catch
block contains
the code that calls foobar
.
Example 3-5. try ... catch Blocks Can Be Implicitly Nested
function foobar( ) { // this is the inner try ... catch block try { // execute some code that might throw an exception... } catch (err) { // handle an error } } // this is the outer try ... catch block try { foobar( ); } catch (err) { // handle any errors thrown by foobar( ) }
In Example 3.5, the catch
block
in the outer try
... catch
block will never be visited, since if any error occurs in
foobar
, the inner catch
block
will respond. There may be times, however, when the inner
try
... catch
block in
foobar
might not want to respond to an error.
For example, when creating reusable scripts using JScript, there may
be instances when you don’t want your reusable script to be
responsible for handling all possible errors. In Chapter 6, we’ll examine a reusable script that
creates generic database administration pages. These scripts work
tightly with a database that is specified by the end developer (that
is, the developer who is utilizing these reusable scripts). If the
end developer specifies a database connection string that is invalid,
it might be best to let the end developer handle the error rather
than attempting to have the reusable script do so. The end developer
might want to try a different connection string, perhaps, and if
passed the error, might be able to recover from it.
To hand off the responsibility of handling an error, simply
re-throw
the error in the catch
block. In the following code snippet, the catch
block is only interested in working with a specific error,
0x800A1234
; all other errors are passed on:
try {some code that might throw an exception
} catch (err) { // only handle error 0x800A1234 if (err.number == 0x800A1234) {handle the error
} else { // pass off the responsibility to the outer try ... catch blockthrow err;
} }
Example 3.6 demonstrates how the responsibility of
handling an error can be passed from an inner try
... catch
block to an outer one.
Example 3-6. To Pass Off the Responsibility of Handling an Exception from an Inner to an Outer try ... catch Block, Re-throw the Exception
<% @LANGUAGE="JSCRIPT" %>
<%
function foobar( )
{
// foobar tries to connect to a database. If it cannot because of an
// an invalid data source name, the catch block re-throws the error, passing
// off the responsibility to the outer catch block...
try {
// attempt to connect to the database...
var objConn;
objConn = Server.CreateObject("ADODB.Connection");
objConn.ConnectionString = "DSN=NonExistentDSN";
objConn.Open( );
} catch(err) {
if ((err.number & 0xFFFF) == 16389)
throw err;
// pass off the responsibility
else {
// not a connection problem... handle the error
}
}
}
// try calling the function ThrowError
try {
// Execute foobar; if there is a problem, the catch block will be visited
foobar( );
} catch (err) {
// at this point, we could check to see if the DSN name was bad, and, if
// so, retry foobar or call some other function
// Instead, for this example, we'll just output the error information
Response.Write("An error occurred! Here are the following details:");
Response.Write("<P><UL><B>Description:</B> " + err.description);
Response.Write("<BR>Error Number:</B> " + (err.number & 0xFFFF));
Response.Write("<BR>Facility Code:</B> " + (err.number >> 16 & 0xF));
}
%>
Now that we’ve examined both VBScript and JScript’s error-handling mechanisms, which one is best? I find JScript’s implementation of error handling to be much more elegant and readable than VBScript’s. Since VBScript’s error-handling approach requires the developer to inquire whether an exception occurred or not after the fact, the approach is more error-prone that JScript’s, where the developer must set up the proper error-handling calls prior to executing the questionable statements.
I also find the JScript syntax far more elegant than VBScript’s error-handling syntax. Of course, to enjoy the benefits of JScript error handling, you do have to use JScript as the server-side scripting language, something only a handful of ASP developers do.
As veteran web surfers, we’ve all stumbled across a broken link, which took us to a nonexistent web page. When clicking on a broken link, an HTTP 404 error is received. This error is generated by the web server when it cannot find a requested document.
Whenever something goes awry deep within a web server, an HTTP error is generated. These HTTP errors are three digits in length, ranging from 100 -505. The first number in the three-number HTTP error code denotes the type of HTTP error. 400-level errors are client errors, such as 404 (file not found), 401 (unauthorized), and 414 (request URL too long). 500-level errors indicate intrinsic web server errors. As we’ll see shortly, IIS 5.0 uses a 500-level HTTP error to signal that an error has occurred on an ASP page.
A complete listing of HTTP error codes and their associated meanings can be seen at http://www.webpartner.com/html/AlertsandErrors.htm.
Prior to ASP 3.0, exception handling on an ASP page was limited to the exception-handling capabilities of the scripting language used. Due to the total lack of error handling in JScript (until Version 5.0) and the verbose nature of error handling in VBScript, ASP was long overdue for a simpler and more effective exception-handling approach. With the release of ASP 3.0 (available exclusively with IIS 5.0 and Windows 2000), a new intrinsic object, ASPError, was added to assist in exception handling. The ASPError object contains nine properties, which are detailed in Table 3.3.
Table 3-3. The Intrinsic ASPError Object Assists in Detecting and Handling Exceptions
Property |
Description |
---|---|
ASPCode |
Returns a rather detailed error code that is generated by IIS |
Number |
Returns the error number |
Source |
Returns the line of code that caused the error, if available |
Category |
Indicates what type of error occurred; this property will indicate if the exception was caused by the scripting language, an external COM component, or an internal ASP error |
File |
Returns the name of the ASP page that generated the error |
Line |
Returns the line number the error occurred on |
Column |
Returns the column the error occurred on |
Description |
Returns a detailed description of the error |
ASPDescription |
Returns a detailed description for those errors that fall under the internal Active Server Pages error Category |
When an error occurs in IIS 5.0, whether it’s from a COM object, the scripting language, or an internal ASP error, an HTTP 500;100 error is generated. (The 100 indicates that it is a special kind of HTTP 500 error.) Whenever an HTTP error occurs, IIS checks an error table to see what it should do in response to the error. Its choices are limited to:
Sending a default message to the client
Sending the client the contents of a particular file (usually an HTML file)
Redirecting the client to an error-handling web page
Through the Internet Information Services, a developer can specify how IIS should handle particular HTTP errors. Start by opening the Internet Information Services (in Windows 2000, go to Start → Programs → Administrative Tools → Internet Services Manager). Right-click on the web site whose HTTP errors you wish to modify and choose Properties. The Web Site Properties dialog box will open; the HTTP error-handling information is under the Custom Errors tab. Figure 3.2 shows a listing of these HTTP errors for IIS on my Windows 2000 machine.
Figure 3-2. The Custom Errors tab in the Web Site Properties dialog box specifies how IIS responds to various HTTP errors
Notice that in Figure 3.2 the 500;100 HTTP error
redirects the user to the URL
/CODEREUSE/Error.asp
. To adjust your settings
for this HTTP error, select the particular error and click the Edit
Properties button. You should now be presented with the Error Mapping
Properties dialog box that contains a list box and a text box. The
list box contains the three various options IIS can take when an HTTP
error occurs: send the default message to the client (Default), send
the client a file (File), or redirect the client to a URL (URL).
Figure 3.3 contains a screenshot of the Error
Mapping Properties dialog box for the HTTP 500;100 error.
With the settings shown in Figure 3.3, whenever an
internal web server error occurs in an ASP page, the HTTP 500;100
error will be tripped. This will send the user to
/CODEREUSE/Error.asp
via an implicit
Server.Transfer
. Figure 3.4
graphically depicts this branch in execution.
Figure 3-4. When an error occurs on an ASP page, an HTTP 500;100 error is generated, branching execution to /CODEREUSE/Error.asp
When an error occurs on an ASP page, not only is a HTTP 500;100 error
generated, but the details of the error are packaged into an
ASPError
object instance. This particular ASPError object instance can be
retrieved in the error-handling web page using the GetLastError
method of the Server object. Example 3.7 contains
the source code for our error-handling web page,
/CODEREUSE/Error.asp
.
Example 3-7. The Error-Handling Web Page Obtains Error Information Using Server.GetLastError
<% @LANGUAGE = "VBSCRIPT" %> <% Option Explicit %> <% 'Grab the instance of the ASPError object that contains info on 'the error that brought us to this error-handling web page Dim objError Set objError = Server.GetLastError( ) %> <FONT SIZE=+1><B>D'oh!</B></FONT><HR NOSHADE> An unexpected error occurred. We apologize for this inconvenience. <P> <!-- Display Error Information --> <TABLE ALIGN=CENTER BORDER=1 CELLSPACING=1> <TR><TH COLSPAN=2>Detailed Error Information</TH></TR> <TR> <TH>Property</TH> <TH>Value</TH> </TR> <TR> <TD>ASPCode</TD> <TD><%=objError.ASPCode%></TD> </TR> <TR> <TD>Number</TD> <TD><%=objError.Number%></TD> </TR> <TR> <TD>Source</TD> <TD><%=objError.Source%></TD> </TR> <TR> <TD>Category</TD> <TD><%=objError.Category%></TD> </TR> <TR> <TD>File</TD> <TD><%=objError.File%></TD> </TR> <TR> <TD>Line</TD> <TD><%=objError.Line%></TD> </TR> <TR> <TD>Column</TD> <TD><%=objError.Column%></TD> </TR> <TR> <TD>Description</TD> <TD><%=objError.Description%></TD> </TR> <TR> <TD>ASPDescription</TD> <TD><%=objError.ASPDescription%></TD> </TR> </TABLE> <% Set objError = Nothing 'Clean up... %>
The error-handling code in Example 3.7 uses the Server.GetLastError method to obtain information on the error that caused the HTTP 500;100 error. Next, the properties of the ASPError object are displayed in an HTML table. As we discussed earlier, the end user shouldn’t be bothered with the technical details of the error, and usually we wouldn’t want to display this detailed information.
As illustrated in Table 3.3, one of the ASPError object’s properties is Category, which indicates which one of three categories the error falls under. The three possible error categories are:
Scripting language error
Internal ASP error
External COM error
For the remainder of this section, we will examine code snippets that generate an error in a unique category. After each code snippet, we’ll examine the output generated by the error-handling script in Example 3.7.
A scripting language error occurs when
code that violates the scripting language’s syntax is
encountered. For example, in VBScript an If
statement has the following syntax:
Ifcondition
thenStatements
[ElseIfcondition
thenElseIf statements
] [ElseElse statements
] End If Or Ifcondition
thenStatements
[ElseElse Statements
]
Writing an If
statement that violates this
expected syntax will result in a scripting language error. Example 3.8 contains the source code to
ScriptingLanguageError.asp
, which is a script
with an illegal If
statement. When
ScriptingLanguageError.asp
is visited through a
browser, the user is automatically redirected to
/CODEREUSE/Error.asp
. A screenshot of a browser
visiting ScriptingLanguageError.asp
can be seen
in Figure 3.5.
Unlike the scripting language error-handling methods, the ASPError
object can be used to detect compile-time errors. This is evident from
the code in Example 3.8, which generates a
compile-time error. Note that, according to the Category property
value listed in Figure 3.5, an improperly formatted
If
statement results in a “Microsoft
VBScript compilation” error. This makes sense, since illegal
syntax generates compile-time errors.
When creating ASP pages, there are certain guidelines that must be
followed regardless of the scripting language used. For example,
server-side includes must have a certain syntax and must reference an
existing file; when using the <%
and
%>
script delimiters, these delimiters must
match (that is, for every <%
there must be a
corresponding %>
).
Internal
ASP errors are a lower level of errors than scripting language
errors. Scripting language errors are dependent upon the syntax of
the particular scripting language used. On the other hand, internal
ASP errors are scripting language-independent. When an internal ASP
error occurs, the ASPDescription property of the
ASPError
object contains extra information further
describing the error.
Example 3.9 contains the source code to
InternalASPError.asp
, a script that tries to
include a file that does not exist. Figure 3.6
contains the output of the script when viewed through a browser. Note
that the ASPDescription property contains information that was not
present in the scripting language error case (Figure 3.5).
Example 3-9. Using a Server-Side Include to Import the Contents of a Nonexistent File Generates an Internal ASP Error
<% @LANGUAGE="VBSCRIPT" %> <% Option Explicit %> <% 'An internal ASP error will be generated since we are attempting to include 'a nonexistent ASP page... %> <!--#include file="NonExistentPage.asp"-->
One of the reasons ASP is such a powerful tool for creating dynamic web pages is its ability to utilize COM components. COM components, like ADO objects, may raise errors if used improperly, or if an external exception is encountered. If such an error occurs in a COM component, the ASPError object classifies it as an external COM object error. The Category property is set to the programmatic identifier of the offending COM component.
Example 3.10 contains the source code for
COMError.asp
, a script that raises an external
COM object error due to the fact that it attempts to close an ADO
Connection object before it has been opened. The output of
COMError.asp
, when visited through a browser,
can be seen in Figure 3.7.
Example 3-10. Attempting to Close a Closed Connection Object Results in an External COM Object Error
<% @LANGUAGE="VBSCRIPT" %> <% Option Explicit %> <% 'The following code snippet will generate a COM object error, since we 'can't close a database connection if it's already closed! Dim objConn Set objConn = Server.CreateObject("ADODB.Connection") objConn.Close %>
3.141.193.158