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 |
---|---|
<cftry> <cf_getEmps EmpName="Jones"> |
In a cftry block, calls the cf_getEmps custom tag (getEmps.cfm). |
<cfcatch type="myApp.getUser.noEmpName"> <h2>Oops</h2> <cfoutput>#cfcatch.Message#</cfoutput><br> </cfcatch> </cftry> |
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 cfthrow tag in the custom tag. |
<cfif isdefined("getEmpsResult")> <cfdump var="#getEmpsResult#"> </cfif> |
If the tag returns a result, uses the cfdump tag to display it. (A production application would not use the cfdump tag.) |
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 the cf_getEmps tag."> <cfexit method = "exittag"> |
Makes sure the calling page specified an EmpName attribute. If not, throws a custom error that indicates the problem and exits the tag. The calling page handles the thrown error. |
<cfelse> <cftry> |
If the tag has an EmpName attribute, does the remaining work inside an outermost try block. The cfcatch block at its end handles any otherwise-uncaught exceptions. |
<cftry> <cfquery Name = "getUser" DataSource="cfdocexamples"> SELECT * FROM Employee WHERE LastName = '#attributes.EmpName#' </cfquery> <cfset caller.getEmpsResult = 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 getEmpsResult variable with the query results. |
<cfcatch type= "Database"> <cfif (cfcatch.sqlstate IS "S100") OR (cfcatch.sqlstate IS "IM002")> <cftry> <cfquery Name = "getUser" DataSource= "cfdocexamplesBackup" SELECT * FROM Employee WHERE LastName = '#attributes.EmpName#' </cfquery> <cfset caller.getEmpsResult = getuser> |
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 SQLState variable value of S100 or IM002). 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 getEmpsResult variable with the query results. |
<cfcatch type = "Database" /> |
If the second database query failed with a database error, gives up silently. Because the Database type cfcatch tag does not have a body, the tag exits. The calling page does not get a getEmpsResult variable. It cannot tell whether the database had no match or an unrecoverable database error occurred, but it does know that no match was found. |
<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 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> |
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 cfcatch tag handles only errors that are rethrown from the nested blocks. Exits the custom tag and returns to the calling page. Ends the catch block, try block, and initial cfif block. |
To test the various ways errors can occur and be handled in this example, try the following: