Transcript Slide 1
Exception Handling MDCFUG 6/12/2007 David Lakein Programmer / Analyst TeraTech Inc. About Me • Application Developer at TeraTech since 1998 • Working in ColdFusion since 1999, CF4.01 • Certified Advanced CF7 Developer What’s wrong with this picture? The Problem • Too much info gets shown to users – Embarrassing – Could be security hazard • Site admins/developers need notification, enough useful debugging info to find the error and be able to fix – Users won’t send it to you, and you don’t want them seeing it anyway! What this presentation will cover • Different types of errors • Language features used to catch and handle – Overview of properties – Suggestions for usage • Creating a simple customizable general error handler What this will not cover • Won’t cover all details and properties of cfcatch and error object • Check out the references at the end Types of errors • Exception: Error that doesn’t allow the request to continue. – You can catch the error, and CF gives you a structure with error details. • Types/examples of errors: – – – – – – Parse/Compile error: malformed CF tags Syntax error: missing property Runtime: won’t be found until code executes Validation: server-side form validation System: connection fails Request: URL that user requested doesn’t exist • Not a CF exception, but CF can handle • Different from CF’s CFError type=Request – Logic errors: don’t directly cause an exception, but are still wrong Error: Parse • ColdFusion first parses the templates, then compiles into Java. If CF cannot parse the syntax, there is little useful it can do with it. • If it’s in the main request page, won’t be caught by CFCatch, CFError, or OnError! – Goes straight to server-wide error handler – Will be caught properly if parse error happens in an include (lower level than the error handler) <cfif 1 eq 3> I have no closing cfif tag. Error: Syntax • Code can be parsed, but language use is wrong, which may prevent it from being compiled. • e.g., Missing required attributes or arguments, invalid parameters <cfbad> CF will say: "Unknown tag: cfbad. ColdFusion cannot determine how to process the tag cfbad because the tag is unknown and not in any imported tag libraries. The tag name might be misspelled…" </cfbad> Error: Runtime • Errors that happen when the code is executing • This covers most other kinds of code errors – Error responses from outside CF • CFHTTP calls • Database – CFML errors • Undefined variables • CFThrow creating a custom exception type • CFAbort with “showerror” attribute • E.g., Value set in in a variable at runtime is the wrong type for a function argument, if it has a type. Error: Validation • CF has basic built-in server-side form validation fields; e.g., hidden fields named “fieldname_required” or “fieldname_type”. • If the submitted data fails, CF throws a Validation error. • Similar feature will be generated by CF7's CFInput validateAt="onServer" • Depends on the extra hidden form fields submitted by client <input type="text" name="firstname" /> <input type="text" name="age" value="Twenty" /> <input type="hidden“ name="firstname_required" value="First name is required."/> <br/> <input type="hidden“ name="age_integer" value="Age must be an integer." /> Error: System • Type of runtime error • Bad connection to db • File is missing Reducing Details Shown By Default • Turn off Robust Exception Information to reduce what gets shown to the user if an error isn’t handled, or if the exception handler fails. Where CF Handles Errors • Server-wide error handler • Server-wide missing template handler – HTTP 404 error • CFError tag – Catches unhandled errors anywhere after the point where it is defined • Application.cfc onError event (CF7+) • Application.cfc onMissingTemplate event (CF8, in public beta) – Local HTTP 404 handler • CFTry/CFCatch – Catch errors right around the code they happen Server-wide error and 404 handlers • Missing Template handler: URL from root of the website – – • HTTP 404 errors Browser requests CF page that doesn’t exist; webserver passes to ColdFusion to handle. Site-wide error handler: ColdFusion path, including mappings, from root. – Template that is executed whenever an error is not handled at the application or code level. CFError • Catches unhandled errors anywhere after the point where it is defined • Declare anywhere in cfm pages. Should be near the top of the Application.cfc or Application.cfm • Object named “Error” – error details, stack trace, location, templates called CFError (cont’d) • Attributes: – Template: CF relative or absolute mapping path to the error handler template – Type – Exception (optional): specific type to catch – Mailto (optional): passes in as error.mailTo • Type: – Type=“Exception” – full exception handler. – Type=“Request” – errors that fail exception handler. Limited; can only output Error variables. – Type=“Validation” – CF form validation. Limited like Request type. CFError example • Application.cfm <cferror type="exception“ template="exception.cfm“ mailTo="[email protected]"> • sample.cfm <h1>Example: cferror in Application.cfm, mail to admin.</h1> <!--- Undefined variable, will cause an error ---> <cfoutput>#NonExistentVariable#</cfoutput> <h3>Since we are using cferror type="Exception", html after the error will not appear, and html before the error will only show in Error.GeneratedContent. </h3> CFError example (cont’d) • Exception.cfm <!--- Default address to send to ---> <cfset mailto=“[email protected]"> <cfif structkeyExists(Error, "mailto") AND Len(error.Mailto) GT 0> <cfset mailto = Error.mailto> </cfif> <cfmail server=“mailserver.domain.com" from="[email protected]" to="#mailto#" subject="Error in Example App on #CGI.HTTP_HOST#" type="html"> <h4>ERROR In page: #Error.Template#<h4> <cfdump var="#error#" label="Error object"> <cfdump var="#cgi#" label="CGI scope"> </cfmail> <cfoutput> <html><head><title>An error occurred.</title> </head> <body> <div style="background-color: ##cc4400; padding: 1em; <h3 style="color: black;">Sorry, there was an error. Site admins have been contacted.</h3> </div> </body> </html></cfoutput> CFError example (cont’d) • What user sees: • What admin gets: Application.cfc onError event (CF 7+) • Arguments: – Exception - exception object • Similar in structure to the CFCatch object you get with cftry/cfcatch – EventName - string • Contains Application event name, if error is in Application.cfc • Otherwise, blank string • No return value • You can/should still have a cferror tag in the declarations section of your Application.cfc; that will run if there is an error in your OnError event. • Overrides all other error handlers besides CFCatch • Check limitations in LiveDocs; no output if runs in onApplicationEnd or onSessionEnd events Application.cfc onError event Example <cffunction name="onError" returnType="void" output="true"> <cfargument name="Exception" required="true"/> <cfargument name="EventName" type="String" required="true" /> <cfoutput>Error caught in Application.cfc, #GetCurrentTemplatePath()# <br/></cfoutput> <cfoutput>Event name: '#Arguments.EventName#'</br></cfoutput> <!--- <cfdump var="#Arguments#" label="Arguments">---> <cfdump var="#Arguments.Exception#" label="#Arguments.EventName# Exception"> <cfdump var="#CGI#" label="CGI"> <cfdump var="#variables#" label="variables in application.cfc"> … </cffunction> Application.cfc onMissingTemplate event (CF 8, beta) • Used as a local application-specific 404 handler: when page specified in URL does not exist. • Arguments: targetPage string. • Return true if handled ok, false if you want to throw a 404 error. – If there is a Missing Template error handler defined in CFAdmin, that will catch. – If that is not defined, CF's built-in default 404 handler will run Application.cfc onMissingTemplate event (CF 8, beta) example <cffunction name="onMissingTemplate"> <cfargument name="targetPage" type="string" required=true/> <!--- Use a try block to catch errors. ---> <cftry> <!--- Log all errors. ---> <cflog type="error" text="Missing template: #Arguments.targetPage#"> <!--- Display an error message. ---> <cfoutput> <h3>#Arguments.targetPage# could not be found.</h2> <p>You requested a non-existent ColdFusion page.<br /> Please check the URL.</p> </cfoutput> <cfreturn true /> <!--- If an error occurs, return false. ---> <cfcatch> <cfreturn false /> </cfcatch> </cftry> </cffunction> CFTry/CFCatch • Wrap the CFTry/CFCatch around a block of code, and it handles errors inside that block • Handles errors locally; can show inside the layout. • Inside the cfcatch tags, you have an object named “cfcatch”, contains error properties and related objects. • You can specify catching certain types only • Rethrow error (tag CFRETHROW) if you want to send the error to a higher level. CFTry/CFCatch sample <cferror type="exception" template="exception.cfm"> <!--- any code out here will be handled by the above exception handler "exception.cfm", or by the Application.cfc OnError if it exists ---> <cfinclude template="test1.cfm"> <cftry> <!--- Any code in here will be handled by these cfcatch blocks ---> <cfinclude template="test2.cfm" <cfcatch type="Any"> <!--- Handle the error here ---> <h3>There was an error with this operation!</h3> <cfdump var="#cfcatch#“ label="CFCatch object!"> </cfcatch> </cftry> <!--- any code out here will be handled by the above exception handler "exception.cfm", or by the Application.cfc OnError if it exists ---> <cfinclude template="test3.cfm" What to do in your error handler • Log – CF already logs basic exception info to exception.log – Could log extra error info to file, one line per error – New in CF8: there will be built-in Derby databases – all inside the CF service, no connections to go down. This may allow for logging other properties than would be doable in a text log, then provide for easy search capability. • Email – HTML formatted email, get a table of all the info you need • Use a common template – Pass in settings for CFMail or logging. – Optionally show debug info for local developers – pass in Debug variable controlled by IP and a URL var • CAUTION: if there is an error in this level, like DB connection, or email malformed, will throw the original error to a higher level! – You won't know about the exception in the error handler. What to send • Scopes: error/cfcatch/exception object, cgi scope, form, other scopes • Simple version: cfdump • Server-wide handler: shouldn’t have a lot of moving parts. – If you check specific optional parts of the error/catch object, check StructKeyExists first! – If you have a more involved template, use try/catch in that, and do a basic action by default. Always handle. CFDump • You may want to write code to parse out the properties of the error/catch object, instead of just dumping. – Highlight most important/useful properties at the top of the email – Potential issue with dumping a CFCatch: There may be an issue with dumping the full object, depending on server installation, version (6.1) – Error detail message may not be clear; also could include HTML content which you won’t want in there. – Process SQL output, show query more visibly. Example 1: CFError included in same file • err1.cfm : <cferror type="exception" template="exception.cfm"> <h1>Example 1: Use cferror in the template</h1> <!--- Undefined variable, will cause an error ---> <cfoutput>#NonExistentVariable#</cfoutput> <h3>Since we are using cferror type="Exception", html after the error will not appear, and html before the error will only show in Error.GeneratedContent.</h3> Example 1 (cont’d) • exception.cfm : <!--- We should be mailing these details (like previous CFError example), and showing only a nice message. ---> <h1>Exception Template Example 1</h1> <h3>Error scope:</h3> <cfdump var="#error#" > <h3>CGI scope:</h3> <cfdump var="#cgi#"> <h4>Error handled at: <cfoutput>#GetCurrentTemplatePath()#</cfoutput> </h4> Example 2: CFError included in Application.cfm • Application.cfm <cfsilent> <cferror type="exception" template="exception.cfm"> </cfsilent> • err2.cfm <h1>Example 2: Use cferror in the Application.cfm</h1> <!--- Undefined variable, will cause an error ---> <cfoutput>#NonExistentVariable#</cfoutput> <h3>Since we are using cferror type="Exception", html after the error will not appear, and html before the error will only show in Error.GeneratedContent. </h3> Example 2 (cont’d) • exception.cfm <!--- We should be mailing these details (like previous CFError example), and showing only a nice message. ---> <h1>Exception Template Example 2</h1> <div style="background-color: #bbbbbb; padding: 2em;"> <h3>Error scope:</h3> <cfdump var="#error#" > <h3>CGI scope:</h3> <cfdump var="#cgi#"> <h4>Error handled at: <cfoutput>#GetCurrentTemplatePath()#</cfoutput> </h4> </div> Example 3: onError event of Application.cfc (CF7+ only) • Application.cfc <cfcomponent name="Application"> <cfset this.name="DLTestFolder"> <cferror type="exception" template="exception.cfm" > <cffunction name="onError" returnType="void" output="true"> <cfargument name="Exception" required="true“ /> <cfargument name="EventName" type="String" required="true"/> <cfoutput>Error caught in Application.cfc, #GetCurrentTemplatePath()# <br/></cfoutput> <cfoutput>Event name: '#Arguments.EventName#'</cfoutput> <cfdump var="#Arguments.Exception#" label="#Arguments.EventName# Exception"> <cfdump var="#CGI#" label="CGI"> </cffunction> </cfcomponent> Example 3 (cont’d) • err3.cfm <h1>Example 3: Use OnError in the Application.cfc</h1> <!--- Undefined variable, will cause an error ---> <cfoutput>#NonExistentVariable#</cfoutput> <h3>Since this error is caught by its Application.cfc OnError event, html after the error will not appear, BUT html before the error will output to the browser. </h3> Example 4: onMissingTemplate event of Application.cfc (CF8 only, in beta) • Application.cfc <cfcomponent name="Application"> <cfset this.name="DLTestFolder"> <cffunction name="onMissingTemplate"> <cfargument name="targetPage" type="string" required="true"/> <!--- Use a try block to catch errors. ---> <cftry> <!--- Log all errors. ---> <cflog type="error" text="Missing template: #Arguments.targetPage#"> <!--- Display an error message. ---> <cfoutput> <h3>#Arguments.targetPage# could not be found.</h2> <p>You requested a non-existent ColdFusion page.<br /> Please check the URL.</p> </cfoutput> <cfreturn true /> <!--- If an error occurs, return false and the default 404 error handler will run. ---> <cfcatch> <cfreturn false /> </cfcatch> </cftry> </cffunction> </cfcomponent> Example 5a, CFTry/CFCatch File error • Application.cfc, like in example 3 • badfile.cfm <h1>Example 5: Try/catch, file error</h1> <!--- Wrap the bad code in try/catch ---> <cftry> <!--- Code that causes the error ---> <cfinclude template="boogabooga.cfm"> <cfcatch type="any"> <!--- If you want to not handle this here, rethrow this error to the higher handler. ---> <!--- <cfrethrow > ---> <cfdump var="#cfcatch#" label="CFCatch in Example 5"> <h4>Error handled at: <cfoutput>#GetCurrentTemplatePath()#, CFCatch</cfoutput></h4> </cfcatch> </cftry> <h3>If there was no error in the Catch block, this should display.</h3> Example 5b, CFTry/CFCatch DB error • Application.cfc like in example 3 • baddb.cfm <h1>Example 5: Try/catch - Database error</h1> <!--- Wrap the bad code in try/catch ---> <cftry> <!--- Code that causes the error; nonexistent table ---> <cfquery name="myquery" datasource="cfdocexamples“ > Select bar FROM foo </cfquery> <cfcatch type="any"> <!--- If you want to not handle this here, rethrow this error to the higher handler. ---> <!--- <cfrethrow > ---> <cfdump var="#cfcatch#" label="CFCatch in Example 5"> <h4>Error handled at: <cfoutput>#GetCurrentTemplatePath()#, CFCatch</cfoutput></h4> </cfcatch> </cftry> <h3>If there was no error in the Catch block, this should display.</h3> Example: CFError, break CFMail • Exception.cfm (like the first CFError example) . . . . <cfmail serRRRver=“mailserver.domain.com" from="[email protected]" to="#mailto#" subject="Error in Example App on #CGI.HTTP_HOST#" type="html"> . . . . . • User sees original error: Other useful info • GetCurrentTemplatePath() – if you have error handling templates multiple places, shows exactly where this error template is. • CGI. HTTP_HOST – Which site this is running on (in subject line) • Other CGI info Caveats • If you have a high-volume site, you may want another solution than emailing you every time, especially for errors that each and every visitor will hit. • If you use CFParam and CFQueryParam on form or URL values: Make sure you handle form validation before this on the server-side. – Provides for nicer validation messages – Form value type errors won’t clutter your inbox, distracting from real errors. References • Livedocs – “Handling Errors” (chapter) http://livedocs.adobe.com/coldfusion/7/htmldocs/00001130.htm – “How ColdFusion handles errors” http://livedocs.adobe.com/coldfusion/7/htmldocs/00001135.htm – CFAdministrator Settings page: global 404 and exception handlers http://livedocs.adobe.com/coldfusion/7/htmldocs/00001705.htm – Application.cfc OnError http://livedocs.adobe.com/coldfusion/7/htmldocs/00000697.htm • Quickdocs: – http://cfquickdocs.com/cferror – http://cfquickdocs.com/cftry • Other presos – Mosh Teitelbaum, Feb 2004: http://mdcfug.org/meetings/AdvancedColdFusionError_Handling.ppt • Google search – “site:livedocs.adobe.com ColdFusion 7 keywords”