Exceptions include any event that disrupts the normal flow of instructions in a ColdFusion page, such as failed database operations, missing include files, or developer-specified events. Ordinarily, when ColdFusion encounters an exception, it stops processing and displays an error message or an error page specified by a cferror tag or the Administrator Site-wide Error Handler setting. However, you can use ColdFusion's exception handling tags to catch and process runtime exceptions directly in ColdFusion pages.
This ability to handle exceptions directly in your application pages enables your application to do the following:
ColdFusion provides the exception-handling tags listed in the following table:
The cftry tag allows you to go beyond reporting error data to the user:
For example, you can use cftry to catch errors in code that enters data from a user registration form to a database. The cfcatch code could do the following:
Code that accesses external resources such as databases, files, or LDAP servers where resource availability is not guaranteed is a good candidate for using try/catch blocks.
In order for your code to directly handle an exception, the tags in question must appear within a cftry block. It is a good idea to enclose an entire application page in a cftry block. You then follow the cftry block with cfcatch blocks, which respond to potential errors. When an exception occurs within the cftry block, processing is thrown to the cfcatch block for that type of exception.
Here is an outline for using cftry and cfcatch to handle errors:
<cftry>
Put your application code here ... <cfcatchtype="exception type1"> Add exception processing code here ... </cfcatch> <cfcatchtype="exception type2"> Add exception processing code here ... </cfcatch> . . . <cfcatchtype="Any"> Add exception processing code appropriate for all other exceptions here ... </cfcatch> </cftry>
Follow these rules and recommendations when you use cftry and cfcatch tags:
cfcatch tags must follow all other code in a cftry tag body.
cftry blocks. For example, the following structure is valid:<cftry>
code that may cause an exception
<cfcatch ...>
<cftry>
First level of exeption handling code
<cfcatch ...>
Second level of exception handling code
</cfcatch
</cftry>
</cfcatch>
</cftry>
If an exception occurs in the first level of exception-handling code, the inner cfcatch block can catch and handle it. (An exception in a cfcatch block cannot be handled by cfcatch blocks at the same level as that block.)
cftry block causes an exception that gets handled by a cfcatch block, and the cfcatch block causes an exception that has no handler, ColdFusion will display the default error message for the exception in the cfcatch block, and you will not be notified of the original exception.cftry/cfcatch combination or reaches the end of the stack.cftry with cfcatch to handle exceptions based on their point of origin within an application page, or based on diagnostic information.cftry tag, including all its cfcatch tags, must be on a single ColdFusion page. You cannot put the <cftry> start tag on one page and have the </cftry> end tag on another page.cfcatch block is not able to successfully handle an error, consider using the cfrethrow tag, as described in "Using the cfrethrow tag".cfcatch tag with no body; for example:<cfcatch Type = Database />
cftry and cfcatch tags to immediately isolate the tag's exceptions.
Within the body of a cfcatch tag, the active exception's properties are available in the cfcatch structure.
The following table describes the variables that are available in most cfcatch blocks:
Note: If you use cfdump to display the cfcatch variable, the display does not include variables that do not have values.
The cfcatch.TagContext variable contains an array of tag information structures. Each structure represents one level of the active tag context at the time when ColdFusion detected the exception. That is, there is one structure for each tag that is open at the time of the exception. For example, if the exception occurs in a tag on a custom tag page, the tag context displays information about the called custom tag and the tag in which the error occurs.
The structure at position 1 in the array represents the currently executing tag at the time the exception was detected. The structure at position ArrayLen(cfcatch.tagContext) represents the initial tag in the stack of tags that were executing when the compiler detected the exception.
The following table lists the tagContext structure attributes:
The following additional variables are available whenever the exception type is database:
| Property variable |
Description |
|---|---|
cfcatch.ErrNumber |
An internal expression error number, valid only when type="Expression". |
The following additional information is available for exceptions related to errors that occur in cflock tags:
The following additional variable is available if the error is caused by a missing file specified by a cfinclude tag:
| Property variable |
Description |
|---|---|
cfcatch.missingFileName |
The name of the missing file. |
The following example shows the cftry and cfcatch tags. It uses the CompanyInfo data source used in many of the examples in this book and a sample included file, includeme.cfm.
If an exception occurs during the cfquery statement's execution, the application page flow switches to the cfcatch type="Database" exception handler. It then resumes with the next statement after the cftry block, once the cfcatch type="Database" handler completes.
Similarly, the cfcatch type="MissingInclude" block handles exceptions raised by the cfinclude tag.
<!--- Wrap code you want to check in a cftry block --->
<cfset EmpID=3><cfparam name="errorCaught" default=""><cftry><cfquery name="test" datasource="CompanyInfo">SELECT Dept_ID, FirstName, LastNameFROM EmployeeWHERE Emp_ID=#EmpID#</cfquery><html><head><title>Test cftry/cfcatch</title></head><body><cfinclude template="includeme.cfm"><cfoutput query="test"><p>Department: #Dept_ID#<br>Last Name: #LastName#<br>First Name: #FirstName#</p></cfoutput><!--- Use cfcatch to test for missing included files. ---><!--- Print Message and Detail error messages. ---><!--- Block executes only if a MissingInclude exception is thrown. ---><cfcatch type="MissingInclude"><h1>Missing Include File</h1><cfoutput><ul><li><b>Message:</b> #cfcatch.Message#<li><b>Detail:</b> #cfcatch.Detail#<li><b>File name:</b> #cfcatch.MissingFileName#</ul></cfoutput><cfset errorCaught = "MissingInclude"></cfcatch><!--- Use cfcatch to test for database errors.---><!--- Print error messages. ---><!--- Block executes only if a Database exception is thrown. ---><cfcatch type="Database"><h1>Database Error</h1><cfoutput><ul><li><b>Message:</b> #cfcatch.Message#<li><b>Native error code:</b> #cfcatch.NativeErrorCode#<li><b>SQLState:</b> #cfcatch.SQLState#<li><b>Detail:</b> #cfcatch.Detail#</ul></cfoutput><cfset errorCaught = "Database"></cfcatch><!--- Use cfcatch with type="Any" ---><!--- to find unexpected exceptions. ---><cfcatch type="Any"><cfoutput><hr><h1>Other Error: #cfcatch.Type#</h1><ul><li><b>Message:</b> #cfcatch.Message#<li><b>Detail:</b> #cfcatch.Detail#</ul></cfoutput><cfset errorCaught = "General Exception"></cfcatch></body></html></cftry>
Use the following procedure to test the code:
cfcatch type="MissingInclude" block displays the error.
cfquery tag, change the line:FROM Employee
to:
FROM Employer
Display the page. This time the cfcatch type="Database" block displays an error message.
Change the cfoutput line:
<p>Department: #Dept_ID#<br>
to:
<p>Department: #DepartmentID#<br>
Display the page. This time the cfcatch type="Any" block displays an error message indicating an expression error.
Open \CFusion\Log\MyAppPage.log in your text editor. You should see a header line, an initialization line, and four detail lines, similar to the following:
"Severity","ThreadID","Date","Time","Application","Message" "Information","web-0","11/20/01","16:27:08",,"C:\Neo\servers\default\logs\ MyAppPage.log initialized" "Information","web-0","11/20/01","16:27:08",,"Page: /neo/MYStuff/NeoDocs/ cftryexample.cfm Error: MissingInclude" "Information","web-1","11/20/01","16:27:32",,"Page: /neo/MYStuff/NeoDocs/ cftryexample.cfm Error: " "Information","web-0","11/20/01","16:27:49",,"Page: /neo/MYStuff/NeoDocs/ cftryexample.cfm Error: Database" "Information","web-1","11/20/01","16:28:21",,"Page: /neo/MYStuff/NeoDocs/ cftryexample.cfm Error: General Exception" "Information","web-0","11/20/01","16:28:49",,"Page: /neo/MYStuff/NeoDocs/ cftryexample.cfm Error: "
The following table describes the code:
You can use the cfthrow tag to raise your own, custom exceptions. When you use the cfthrow tag, you specify any or all of the following information:
All of these values are optional. You access the attribute values in cfcatch blocks and Exception type error pages by prefixing the attribute with either cfcatch or error, as in cfcatch.extendedInfo. The default ColdFusion error handler displays the message and detail values in the Message pane and the remaining values in the Error Diagnostic Information pane.
The cfcatch tag catches a custom exception when you use any of the following values for the cfcatch type attribute:
cfthrow tag.
cfthrow tag. For more information, see the next section, "Custom error type name hierarchy" .Application, which matches an exception that is thrown with the Application type attribute or with no type attribute.Any, which matches any exception that is not caught by a more specific cfcatch tag.
Similarly, if you specify any of these types in a cferror tag, the specified error page will display information about the thrown error.
Because the cfthrow tag generates an exception, a Request error handler or the Site-wide error handler can also display these errors.
You can name custom exception types using a method that is similar to Java class naming conventions: domain name in reverse order, followed by project identifiers, as in the following example:
<cfthrow
type="com.myCompany.myApp.Invalid_field.codeValue" errorcode="Dodge14B">
This fully qualified naming method is not required; you can use shorter naming rules, for example, just myApp.Invalid_field.codeValue, or even codeValue.
This naming method is not just a convention however. The ColdFusion Server uses the naming hierarchy to select from a possible hierarchy of error handlers. For example, assume you use the following cfthrow statement:
<cfthrow type="MyApp.BusinessRuleException.InvalidAccount">
Any of the following cfcatch error handlers would handle this error:
<cfcatchtype="MyApp.BusinessRuleException.InvalidAccount">
<cfcatchtype="MyApp.BusinessRuleException"> <cfcatchtype="MyApp">
The handler that most exactly matches handles the error. Therefore, in this case, the MyApp.BusinessRuleException.InvalidAccount handler gets invoked. However, if you used the following cfthrow tag:
<cfthrow type="MyApp.BusinessRuleException.InvalidVendorCode
the MyApp.BusinessRuleException handler receives the error.
The type comparison is no case-sensitive.
Use the cfthrow tag when your application can identify and handle application-specific errors. One typical use for the cfthrow tag is in implementing custom data validation. The cfthrow tag is also useful for throwing errors from a custom tag page to the calling page.
For example, on a form action page or custom tag used to set a password, the application can determine whether the password entered is a minimum length, or contains both letters and number, and throw an error with a message that indicates the password rule that was broken. The cfcatch block handles the error and tells the user how to correct the problem.
The cfrethrow tag lets you create a hierarchy of error handlers. It tells ColdFusion to exit the current cfcatch block and "rethrow" the exception to the next level of error handler. Thus, if an error handler designed for a specific type of error cannot handle the error, it can rethrow the error to a more general-purpose error handler. The rethrow tag can only be used in a cfcatch tag body.
The following pseudo-code shows how you can use the cfrethrow tag to create an error-handling hierarchy:
<cftry>
<cftry>
Code that might throw a database error
<cfcatch Type="Database">
<cfif Error is of type I can Handle>
Handle it
<cfelse>
<cfrethrow>
</cfif
</cfcatch>
</cftry>
<cfcatch Type="Any">
General Error Handling code
</cfcatch>
</cftry>
Although this example uses a Database error as an example, you can use any cfcatch type attribute in the innermost error type.
Follow these rules when you use the rethrow tag:
cftry tags, with one tag for each level of error handling hierarchy. Each level contains the cfcatch tags for that level of error granularity.
cftry block.cftry block.cftry block.cfcatch block except those in the outermost cftry block with a cfrethrow tag.
The following example shows many of the techniques discussed in this chapter, including nested cftry blocks and the cfthrow and cfrethrow tags. The example includes a simple calling page and a custom tag page:
The calling page represents a section from a larger application page. To keep things simple, the example hard-codes the name to be looked up.
<cftry>
<cf_getEmps EmpName="Jones">
<cfcatch type="myApp.getUser.noEmpName">
<h2>Oops</h2>
<cfoutput>#cfcatch.Message#</cfoutput><br>
</cfcatch>
</cftry>
<cfif isdefined("getEmpsResult")>
<cfdump var="#getEmpsResult#">
</cfif>
The following table describes the code:
The custom tag page searches for the name in the database and returns any matching records in a getEmpsResult variable in the calling page. It includes several nested cftry blocks to handle error conditions. For a full description, see "Reviewing the code", following the example:
Save the following code as getEmps.cfm in the same directory as the calling page.
<!--- If the tag didn't pass an attribute, throw an error to be handled by
the calling page --->
<cfif NOT IsDefined("attributes.EmpName")>
<cfthrow Type="myApp.getUser.noEmpName"
message = "Last Name was not supplied to the cf_getEmps tag.">
<cfexit method = "exittag">
<!--- Have a name to look up --->
<cfelse>
<!--- Outermost Try Block --->
<cftry>
<!--- Inner Try Block --->
<cftry>
<!--- Try to query the main database and set a caller variable to the result --->
<cfquery Name = "getUser" DataSource="CompanyInfo">
SELECT *
FROM Employee
WHERE LastName = '#attributes.EmpName#'
</cfquery>
<cfset caller.getEmpsResult = getuser>
<!--- If the query failed with a database error, check the error type
to see if the database was found --->
<cfcatch type= "Database">
<cfif (cfcatch.SQLState IS "S100") OR (cfcatch.SQLState IS
"IM002")>
<!--- If the database wasn't found, try the backup database --->
<!--- Use a third-level Try block --->
<cftry>
<cfquery Name = "getUser" DataSource="CompanyInfoBackup">
SELECT *
FROM Employee
WHERE LastName = '#attributes.EmpName#'
</cfquery>
<cfset caller.getEmpsResult = getuser>
<!--- If still get a database error, just return to the calling page
without setting the caller variable. There is no cfcatch body.
This might not be appropriate in some cases.
The Calling page ends up handling this case as if a match was not
found --->
<cfcatch type = "Database" />
<!--- Still in innermost try block. Rethrow any other errors to the next
try block level --->
<cfcatch type = "Any">
<cfrethrow>
</cfcatch>
</cftry>
<!--- Now in second level try block.
Throw all other types of Database exceptions to the next try
block level --->
<cfelse>
<cfrethrow>
</cfif>
</cfcatch>
<!--- Throw all other execptions to the next try block level --->
<cfcatch type = "Any">
<cfrethrow>
</cfcatch>
</cftry>
<!--- Now in Outermost try block.
Handle all unhandled exceptions, including rethrown exceptions, by
displaying a message and exiting to the calling page.--->
<cfcatch Type = "Any">
<h2>Sorry</h2>
<p>An unexpected error happened in processing your user inquiry.
Please report the following to technical support:</p>
<cfoutput>
Type: #cfcatch.Type#
Message: #cfcatch.Message#
</cfoutput>
<cfexit method = "exittag">
</cfcatch>
</cftry>
</cfif>
The following table describes the code:
To test the various ways errors can occur and be handled in this example, try the following:
cfquery tag, change the data source name to an invalid data source; for example, NoDatabase.cfquery tag to CompanyInfo.cfthrow tags throwing custom exculpations in various places in the code and observe the effects.