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:
Code | Description |
---|---|
|
In a |
|
If the tag throws an exception indicating that it did not receive a valid attribute, catches the exception and displays a message, including the message variable set by the |
|
If the tag returns a result, uses the |
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="cfdocexamples"> 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="cfdocexamplesBackup"> 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 exceptions 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:
Code | Description |
---|---|
<cfif NOT IsDefined("attributes.EmpName")> cfthrow Type="myApp.getUser.noEmpName" message = "Last Name was not supplied to |
Makes sure the calling page specified an |
<cfelse> <cftry> |
If the tag has an |
<cftry> <cfquery Name = "getUser" |
Starts a second nested try block. This block catches exceptions in the database query. If there are no exceptions, sets the calling page's |
<cfcatch type= "Database"> <cfif (cfcatch.sqlstate IS "S100") OR (cfcatch.sqlstate IS "IM002")> <cftry> <cfquery Name = "getUser" DataSource= |
If the query threw a Database error, checks to see if the error was caused by an inability to access the database (indicated by an If the database was not found, starts a third nested try block and tries accessing the backup database. This try block catches exceptions in this second database access. If the database inquiry succeeds, sets the calling page's |
<cfcatch type = "Database" /> |
If the second database query failed with a database error, gives up silently. Because the Database type |
<cfcatch type = "Any"> <cfrethrow> </cfcatch> </cftry> |
If the second database query failed for any other reason, throws the error up to the next try block. Ends the innermost try block |
<cfelse> <cfrethrow> </cfif> </cfcatch> |
In the second try block, handles the case in which the first database query failed for a reason other than a failure to find the database. Rethrows the error up to the next level, the outermost try block. |
<cfcatch type = "Any"> <cfrethrow> </cfcatch> </cftry> |
In the second try block, catches any errors other exceptions and rethrows them up to the outermost try block. Ends the second try block. |
<cfcatch Type = "Any"> <h2>Sorry</h2> <p>An unexpected error happened in processing |
In the outermost try block, handles any exceptions by displaying an error message that includes the exception type and the exception's error message. Because there was no code to try that is not also in a nested try block, this Exits the custom tag and returns to the calling page. Ends the catch block, try block, and initial |
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 cfdocexamples.
cfthrow
tags throwing custom exculpations in various places in the code and observe the effects.