420 likes | 431 Views
Learn how to handle errors in your ColdFusion applications effectively, preventing user exposure and ensuring easy debugging. This presentation covers different types of errors, language features used to catch and handle them, and suggestions for usage. Discover how to create a simple customizable general error handler.
E N D
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
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 • sample.cfm <cferror type="exception“ template="exception.cfm“ mailTo="dl@domain.com"> <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=“admin@domain.com"> <cfif structkeyExists(Error, "mailto") AND Len(error.Mailto) GT 0> <cfset mailto = Error.mailto> </cfif> <cfmail server=“mailserver.domain.com" from="admin@domain.com" 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 • baddb.cfm • Application.cfc like in example 3 <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="admin@domain.com" 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 OnErrorhttp://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”