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 ... <cfcatch
type
="exception type1"> Add exception processing code here ... </cfcatch
> <cfcatch
type
="exception type2"> Add exception processing code here ... </cfcatch
> . . . <cfcatch
type
="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, LastName
FROM Employee
WHERE 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:
<cfcatch
type
="MyApp.BusinessRuleException.InvalidAccount">
<cfcatch
type
="MyApp.BusinessRuleException"> <cfcatch
type
="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.