One Step Beyond: CFERROR Custom Error Messages (with Something Extra)

By Bouton Jones

© 2009, 2012 Bouton Jones

 
 

Preface

This article is about error customization through CFERROR and the Application template (using CF MX 7.)

NOTE: There are several methods for customizing error messages but this workshop will ONLY focus on CFERROR customization though the Application.cfm / Application.cfc file. Although at the end of the work shop I will list other methods for developing customized error messages.

This article is accessible for beginner ColdFusion developers and it assumes a beginner developer's understanding of ColdFusion. I assume you can create a simple template, add a few CF tags, create a Application.cfm or Application.cfc template, and debug your errors. But while the target of this workshop is begining developers, I hope more advanced users can benefit from this article.   When I implemented the Custom Error procedure, I started with Chapter 19 "Introducing the Web Application Framework" (pgs. 542-551) in Macromedia ColdFusion MX 7 Web Application Construction Kit by Ben Forta & Raymond Camden. But I expanded upon the material.

Typically, the default ColdFusion Error message is ugly and alarming.   Customizing error messages can make a more pleasant experience for the user and a more robust solution for the developer --- but they come with new challenges and liabilities.   While the basic technique in this workshop is straight from a Forta book, the presentation implements several improvements and techniques that Forta didn't cover as well as tips for addressing some of the hidden challenges inherant with these techniques.

(After writting most of this article, I found that another developer had arrived at a similar solution before me:

CF Error Handling - A pinch of this, a pinch of that
By: Al DiMarzio, HB Graphics
Guilford, CT
http://www.hbgraphics.com/tips_tutorials/ 9-cf_error_error_handling.cfm#2

As the saying goes "Great minds think alike.")

 

Files

Rather than reinventing the wheel, I suggest you download the templates I've already created and debugged and adapt them to your own situations.

Download Custom Error Templates

 

Default ColdFusion Error Messages

Here are some screen captures of error messages before customization:

First Ugly Error Message

Second Ugly Error Message

Aren't they pretty?

[Hint: No.]

 

What Are the Benefits?

What are the benefits of customized error messages over the default ColdFusion error messages?

  • More professional and attractive appearance.  (They don't look broken.)

  • More user friendly.  They addresses the user directly with comprehensible information.

  • More comforting:  They reassures the user that we are aware of the problem and will act to fix it.

  • Faster:  They notify the developer quicker.  (Usually before the client has time to call the application owner and the application owner has time to contact the developer)

  • More efficient:  Collects valuable information about a programming error for the developer without having the developer interview the user.

  • More proactive:  Tracks user validation errors.  Is someone trying to crack your web form?  Are a lot of users having the same problem submitting a form?  (I.E. are several users consistently getting confused by the same instructions? That could be a sign that the instructions need clarification.)

 

Caveats

  • Two of the three custom error templates are limited in the ColdFusion tags and variables they can use. This limits their customization and functionality. (Fortunately there is a way around these limitation which this article will address. That is the part of the presentation that goes "beyond Forta.")

  • Custom error messages are less informative that the ugly default error messages. I recommend temporarily disabling the custom error messages once you're aware of an error and restore the templates once you've identified a solution.

  • You must debug the error templates before making them live. If you have any errors in the custom error templates when you call them though the CFERROR tags, they will generate an infinite loop: A blank browser window and a persistant "reload" noise.

 

Steps

Development can be broken down into five major steps.

  1. Create four or five new templates. The first three are the "Custom Error" Templates (a Request Error template, an Exception Error template, and a Validation Error template as described by Forta)

  2. Debug each template by opening them in a browser.

  3. Add the three CFERROR tags to your Application.cfc file. (Or your Application.cfm if you haven't gotten around to incorporating Application.cfc.)

  4. Further test and debug on development. Don't wait for errors. Create errors where there are none.

  5. Further test and debug on production. Create errors even in applications that don't have any.

 

Definitions

Request Error
Handles any exception that is not otherwise-handled. The request error page runs after the CFML language processor finishes. As a result, the request error page cannot include CFML tags, but can display error page variables. A request error page is useful as a backup if errors occur in other error handlers.


Exception Error
Handles specific exception errors. You can specify individual error pages for different types of exceptions.


Validation Error
Handles server-side form field data validation errors. The validation error page cannot include CFML tags, but it can display error page variables. You can use this attribute only on the Application.cfm page. It has no effect when used on any other page. Therefore, you can specify only one validation error page per application, and that page applies to all server-side validation errors.

[Source: Adobe Livedocs]

 

Error Variables Info

(From the Adobe Website)

The exception-handling template specified in the template attribute of the CFERROR tag contains one or more error variables. ColdFusion substitutes the value of the error variable when an error displays.

The following table lists the error variables:

Error Scope Variables
Template type
Error variable
Description
Exception
Request
Monitor
error.diagnostics
Detailed error diagnostics from ColdFusion Server.
error.mailTo
E-mail address of administrator notified (corresponds to the value set in the mailTo attribute of CFERROR).
error.dateTime
Date and time when the error occurred.
error.browser
Browser that was running when the error occurred.
error.generatedContent
The failed request's generated content.
error.remoteAddress
IP address of the remote client.
error.HTTPReferer
Page from which the client accessed the link to the page where the error occurred.
error.template
Page being executed when the error occurred.
error.queryString
URL query string of the client's request.
Validation
error.validationHeader
Text for header of validation message.
error.invalidFields
Unordered list of validation errors that occurred.
error.validationFooter
Text for footer of validation message.

[Source: Adobe Livedocs]

 

Step One: Making the Exception Error Template

The most effective means of developing a set of customized error message templates is to adapt a working set of templates to your own needs.  Example templates are availalble for download above.   The Exception Error Template will use a special set or variables in the ERROR scope that help in debugging the error.  In addition you can all of the other ColdFusion variables, attributes, and tags that can help you --- including the CGI variables and the CFMAIL tag.

Initial Exception Error Template

<!---
Exception Error Display Template: Any CF functions,
variables, or tags: including ERROR scope variables
and CFMAIL
--->

<!---
1. Notify the developer of the exception error via email
NOTE: ERROR.Mailto defined in application.cfm
--->
<cfif isDefined("ERROR.Mailto") AND ERROR.MailTo NEQ "" AND SESSION.UserEmail NEQ "">
     <cfmail to="#ERROR.MailTo#" from="#SESSION.UserEmail#" subject="Exception Error" type="html">
                <p>Exception Error</p>
                <p>URL: http<cfif CGI.HTTPS EQ
                        "on">s</cfif>://#CGI.server_name##ERROR.Template#<cfif
                        isDefined("ERROR.QueryString") AND ERROR.QueryString
                        NEQ ''>?#ERROR.QueryString#</cfif></p>
                <p>User: #CGI.REMOTE_USER#</p>
                <p>Email Date/Time: #dateformat(NOW(),"yyyy-mm-dd")#
                #timeformat(now(), "HH:MM")#</p>
                <p>Error Date/Time: #dateformat(ERROR.DateTime,"yyyy-mm-dd")#
                        #timeformat(ERROR.DateTime, "HH:MM")#</p>
                <p>IP Address: #CGI.REMOTE_ADDR#</p>
                <p>User’s Browser: #ERROR.Browser#</p>
                <p>------------------------------------</p>
                <p>#ERROR.Message#</p>
     </cfmail>
</cfif>

<!--- 2. Notify the user of the exception error via a web based error mesage --->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<HEAD>
     <TITLE>Exception Error</TITLE>
     <link href="style.css" rel="stylesheet" type="text/css" />
</HEAD>

<cfinclude template="header.cfm">

<table width="100%" cellspacing="0" border="0" cellpadding="3">
     <tr>
          <td width="200" class="navbar_header">Web Application Name</td>
          <td class="header"> </td>
     </tr>
     <tr>
          <td width="200" valign="top" height="420"><!---Menu Component--->
               <cfinclude template="menu.cfm">
          </td>
          <td valign="top">
          
               <div align="center">
               
                    <TABLE class="err_table">
                         <caption>An error has occurred that prevents us from completing
                              your request.</caption>
                         <TR>
                              <TD style="text-align:left">
                              
                                   <p>Exception Error<br />
                                   <br />
                                   Please cut and paste this error message into an email
                                   and provide us with any additional information that
                                   would help us fix this problem (E.G. what you were
                                   trying to do when the error occurred, any data you
                                   included in a form, etc.,) Please send E-Mail to:                                    <cfoutput>
                                   <A HREF="mailto:#ERROR.MailTo#?subject=Exception
                                   <Error">#ERROR.MailTo#</A>.<br />
                                   <br />
                                   ERROR MESSAGE: #ERROR.Message#<br />
                                   <br />
                                   URL: http<cfif CGI.HTTPS EQ
                                   "on">s</cfif>://#CGI.server_name##ERROR.Template#<cfif
                                   isDefined("ERROR.QueryString") AND ERROR.QueryString
                                   NEQ ''>?#ERROR.QueryString#</cfif><br />
                                   <br />
                                   DATE and TIME: #dateformat(NOW(),"yyyy-mm-dd")#
                                   #timeformat(now(), "HH:MM")#
                                   </cfoutput>
                                   </p>
                    
                              </TD>
                         </TR>
                    </TABLE>
               </div>
     
          </td>
     </tr>
</table>

<br />
<cfinclude template="footer.cfm">

</body>
</html>

 

And here's the resulting error message (more or less):

 
Custom Error Message

 

Step One (Continued): Request and Validation Error Templates

Unfortunately, the code for the other two templates are more limited and less rebust.   The Request Error template and the Validation Error template only recognize a small set of CF variables (in the ERROR scope.)   They do not allow very many ColdFusion tags (such as the CFINCLUDE and CFMAIL), ColdFusion functions, or any other variables.   Notice that our customization is limited (no CFINCLUDE) and we can't send an email to the developer without the user's intervention (no CFMAIL.)

 

 

 

Bummer!

HOWEVER!   The Request Error template and the Validation Error template can utilize plain HTML form tags and JavaScript.   So it's possible for those templates to pass information about the errors to another ColdFusion template that isn't limited in the ways that the Request and Validation error templates are limited.   The second template can display the error in a customized format and send an email to the developer just like the Exception Error template!

Request Error Template

<!---
Request Error Display Template: Very few ColdFusion functions, variables, or tags work in this template --- excepting a few specific ERROR scope variables and apparently CFOUTPUT
--->

<!---
For debugging this template. Errors in this template will lead to an infinate loop. So it's best to test this template directly before making the changes live. First comment out the CFERROR tag in Application.cfm and then visit this page by typing the URL in the address bar. Once the template is fully debugged, comment out the CFPARAMs. (Remember, normally this template won't work with CF tags.)
--->
<!---
<cfparam name="ERROR.MailTo" default="webmaster@mydomain.com">
<cfparam name="ERROR.DateTime" default="">
<cfparam name="ERROR.Template" default="">
<cfparam name="ERROR.QueryString" default="">
<cfparam name="ERROR.RemoteAddress" default="">
<cfparam name="ERROR.HTTPReferer" default="">
<cfparam name="ERROR.Browser" default="">
<cfparam name="ERROR.Diagnostics" default="">
 --->

<!--- Form passes error variables to dspCustomError.cfm. Form is submitted automatically by the onload event handler. --->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<HEAD>
        <TITLE>Request Error!</TITLE>
</HEAD>
<!--- Submit the form immediately and automatically --->
<BODY onload="document.goform.submit()">

<!--- Form to pass the request error information to another template --->
<form id="goform" name="goform" method="post" action="dspCustomError.cfm">
     <input type="hidden" name="ErrorType" value="Request" />
     <cfoutput>
          <input type="hidden" name="MailTo" value="#ERROR.MailTo#" />
          <input type="hidden" name="Datetime" value="#ERROR.Datetime#" />
          <input type="hidden" name="Template" value="#ERROR.Template#" />
          <input type="hidden" name="QueryString" value="#ERROR.QueryString#" />
          <input type="hidden" name="RemoteAddress"
          value="#ERROR.RemoteAddress#" />
          <input type="hidden" name="HTTPReferer" value="#ERROR.HTTPReferer#" />
          <input type="hidden" name="Browser" value="#ERROR.Browser#" />
          <input type="hidden" name="Diagnostics" value="#ERROR.Diagnostics#" />
     </cfoutput>
</form>

</BODY>
</HTML>

 

Validation Error Template

<!---
Validation Error Template: No CF tags or functions.
Just a handful of ERROR scope variables.
NOTE to DEVELOPER: There is no need to for you to customize this
template to match the appearance of your web app. / web site.
The form passes error variables to dspCustomError.cfm.
The form is submitted automatically by the onload event handler.
--->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<HEAD>
    <TITLE>Validation Error!</TITLE>
</HEAD>
<!--- Submit the form immediately and automatically --->
<BODY onload="document.goform.submit()">

<!--- Form to pass the validation error information to another template --->
<form id="goform" name="goform" method="post" action="dspCustomError.cfm">
     <input type="hidden" name="ErrorType" value="Validation" />
     <!--- Validation errors do not use the ERROR.MailTo variable. --->
     <cfoutput>
          <input type="hidden" name="ValidationHeader"
               value="#ERROR.ValidationHeader#" />
          <input type="hidden" name="InvalidFields"
               value="#ERROR.InvalidFields#" />
     </cfoutput>
     <SCRIPT LANGUAGE="JAVASCRIPT" TYPE="TEXT/JAVASCRIPT">
          if (document.referrer != '')
          document.write('<input type="hidden" name="ReferringPage" value="' +
               document.referrer + '">');
     </SCRIPT>
</form>

</BODY>
</HTML>

 

Side Note: Server Side and Client Side Data Validation

The Validation Error template is strictly for server side validation.   A best practice is to catch validation errors with a mixture of client side (JavaScript) and server side validation.   Most of us use client side validation but we should augment it with server side validation in case the user turns off JavaScript --- or if the client decides to forbid the use of JavaScript on it's networks.  (Heaven Forbid!)   Here's one quick and easy technique:

  1. Develop a template that uses CFFORM and CFINPUT tags to validate the form fields on the server. Be sure to write customized error messages that are easy to read and understand. Select the onServer option for the validateAt attribute.  Access the pages over the intranet in a web browser.  View the source.

  2. "Cut" the FORM markup in the source and "paste" it into the template (replacing the CFFORM and CFINPUT tags with FORM and INPUT tags.  Please note ColdFusion should have added new INPUT tags of the hidden type.

Before:

<cfform action="Test_for_SQL_Injection.cfm" method="post">
SSN: <cfinput name="SSN" id="SSN" type="text" validateat="onserver"
required="yes" message="Please enter a valid SSN"
validate="social_security_number">
<cfinput name="submit" type="submit" value="submit">
</cfform>

After:

<form action="Test_for_SQL_Injection.cfm" method="post">
SSN: <input name="SSN" id="SSN" type="text" validateat="onserver" required="yes"
message="Please enter a valid SSN" validate="social_security_number">
<input type='hidden' name='SSN_CFFORMSSN' value='Please enter a valid SSN'>
<input type='hidden' name='SSN_CFFORMREQUIRED' value='Please enter a valid SSN'>

<input name="submit" type="submit" value="submit">
</form>

Even if you don't like the validation JavaScript that ColdFusion generates or if it's against a policy (private or otherwise) to use canned JavaScript, you can still take advantage of ColdFusion's server side validation. Replacing the CF markup with HTML that does the same thing, reduces the load on your ColdFusion server. It's less markup for it to parse.

Step One (Continued): Monitor Errors

Some documentation online mentions the Monitor type error and suggests it as an attribute type for the CFERROR tag.

<CFERROR TYPE="MONITOR" TEMPLATE="MONITOR.cfm">

Don't use it.

Livedocs reads in part: "Macromedia recommends that customers do not use CFERROR type='monitor' within CFML templates."   It's depreciated.

 

Step One (Continued): the Fourth Template

As we've learned, the traditional implemenation of CFERROR templates is to create three custom error message templates. An improvement we can make is to add a fourth template which took the data from the three original templates, emailed the information to the developer and then displayed the information to the user.

What belongs in this fourth template?

  • CFPARAM tags for all variables in case crackers try to access the template directly. Or if a user makes a bookmark of the error page. Or something unexpected happens.

  • Optionally, a CFQUERY insert (See below)

  • CFMAIL email code to send a complete error message and diagnostic info to the developer without the user's intervention.

  • A message to the user that is professional, conversational, and limits "Too Much Information." (Maybe the message to the user should include a request for the user to email the developer with details about the error.)

SIDE NOTE: What is "Too Much Information?" What does the user need to know?  The developer needs to include information that explains the problem to the user succinctly but doesn't give any information that will confuse the user and might be abused by a malicious cracker.

ANOTHER SIDE NOTE: Why ask the user to contact the developer if the developer is going to automajically get an email anyway? Because Redundancy is your friend. And your friend is redundancy. If you depend entirely on an automated email solution to notify yourself of errors, inevitably it will fail.

Here's an example of the fourth template.

The Fourth Template: dspCustomError.cfm

<!---
dspCustomError.cfm for Exception, Request, and Validation errors.  It displays error information passed from other templates, emails the error information to the developer, and displays the error message to the user.
--->

<cfparam name="URL.action" default="Unknown">
<cfoutput><cfparam name="LOCAL.action" default="#URL.action#"></cfoutput>
<cfparam name="LOCAL.action" type="regex" pattern="[A-Za-z0-9_]{3,30}">
<cfparam name="FORM.ErrorType" default="Unknown">

<!---
1. In case this template is accessed directly through a favorite, bookmark, etc., establish a default email address from which to send emails. CheckOutUser is part of the application's login system.
--->
<cfparam name="SESSION.UserEmail" type="email" default="HelpDesk@MyDomain.com">

<!---
2. Send email to developer with information about the error (Exception, Request, or Validation error.)
--->
<cfswitch expression="#FORM.ErrorType#">

     <cfcase value="Request">
     
          <cfparam name="FORM.HTTPReferer" default="">
          <cfparam name="FORM.Template" default="">
          <cfparam name="FORM.QueryString" default="">
          <cfparam name="FORM.Datetime" default="">
          <cfparam name="FORM.RemoteAddress" default="">
          <cfparam name="FORM.Browser" default="">
          <cfparam name="FORM.Diagnostics" default="">
          <cfparam name="FORM.MailTo"
               default="Webmaster@MyDomain.com">
          <cfparam name="FORM.MailTo" type="email">
          
          <cfif FORM.MailTo NEQ "" OR SESSION.UserEmail NEQ "">
               <cfmail to="#FORM.MailTo#"
                    from="#SESSION.UserEmail#"
                    subject="Request Error"
                    type="html">
               
                    <p>#FORM.ErrorType# Error</p>
                    <p>USER: #CGI.REMOTE_USER#</p>
                    <p>ERROR DATE and TIME:
                    #DateFormat(FORM.Datetime,"yyyy-mm-dd")#
                    #TimeFormat(FORM.Datetime, "HH:MM")#
                    CST</p>
                    <p>EMAIL DATE and TIME:
                    #DateFormat(NOW(),"yyyy-mm-dd")#
                    #TimeFormat(NOW(), "HH:MM")#
                    CST</p>
                    <p>REFERRING PAGE: #FORM.HTTPReferer#</p>
                    <p>ERROR PAGE: http<cfif CGI.HTTPS EQ
                    "on">s</cfif>://#CGI.server_name##FORM.Template#<cfif
                    isDefined("FORM.QueryString")
                    AND FORM.QueryString
                    NEQ ''>?#FORM.QueryString#</cfif></p>
                    <p>IP NUMBER: #FORM.RemoteAddress#</p>
                    <p>BROWSER: #FORM.Browser#</p>
                    <p>-------- BEGIN DIAGNOSTICS ---------</p>
                    <p>Diagnostics: #FORM.Diagnostics#</p>
                    <p>--------- END DIAGNOSTICS ----------</p>
               
               </cfmail>
          </cfif>
     
     </cfcase>
     <cfcase value="Validation">
     
          <!---
          Note: Validation errors do NOT use the ERROR.MailTo variable.
          --->
          <cfparam name="FORM.ValidationHeader" default="">
          <cfparam name="FORM.InvalidFields" default="">
          <cfparam name="FORM.ValidationFooter" default="">
          
          <cfif SESSION.MailTo NEQ "" OR SESSION.UserEmail NEQ "">
          
               <cfmail to="#SESSION.MailTo#"
                    from="#SESSION.UserEmail#"
                    subject="Validation Error"
                    type="html">
               
               <p>Validation Error(s)</p>
               <p>USER: #CGI.REMOTE_USER#</p>
               <p>DATE and TIME:
               #DateFormat(NOW(),"yyyy-mm-dd")#
               #TimeFormat(NOW(), "HH:MM")#
               CST</p>
               <p>DATE and TIME:
               #dateformat(NOW(),"yyyy-mm-dd")#
               #timeformat(now(), "HH:MM")#</p>
               <p>EMAIL DATE and TIME:
               #dateformat(NOW(),"yyyy-mm-dd")#
               #timeformat(now(), "HH:MM")#</p>
               <p>USER IP ADDRESS: #CGI.REMOTE_ADDR#</p>
               <p>USER BROWSER: #CGI.HTTP_USER_AGENT#</p>
               <p>URL: #FORM.HTTPReferer#</p>
               <p>------------------------------------</p>
               <p>Invalid Fields: #FORM.InvalidFields#</p>
               
               </cfmail>
          
          </cfif>
     
     </cfcase>

</cfswitch>

<!---
3. Present error message to user: either Exception, Request, or Validation error
--->

<h1 style="text-align:center"><cfoutput>#FORM.ErrorType#</cfoutput> Error</h1>

<div align="center">

     <TABLE class="err_table">
          <caption>An error has occurred that prevents us from completing your request.</caption>
          <TR><TD>
          
               <cfswitch expression="#FORM.ErrorType#">
               
                    <cfcase value="Request">
                    
                         <cfoutput>
                         
                              <p>Please cut and paste this error message into an email
                              and provide us with any additional information that
                              would help us fix this problem (E.G. what you
                              were trying to do when the error occured, any data you
                              included in a form, etc.,)   Send the email to
                              <A HREF="mailto:#FORM.MailTo#?subject=Request
                              Error">#FORM.MailTo#</A>.</p>
                              
                              <p>DATE and TIME:
                              #DateFormat(NOW(),"yyyy-mm-dd")#
                              #TimeFormat(NOW(), "HH:MM")#
                              CST</p>
                              
                              <p>REFERRING PAGE: #FORM.HTTPReferer#</p>
                              
                              <p>ERROR PAGE: http<cfif CGI.HTTPS EQ
                              "on">s</cfif>://#CGI.server_name##FORM.Template#<cfif
                              isDefined("FORM.QueryString") AND FORM.QueryString
                              NEQ ''>?#FORM.QueryString#</cfif></p>
                              <p>------------------------------------</p>
                              <p>DIAGNOSTICS: #FORM.Diagnostics#</p>
                         
                         </cfoutput>
                    
                    </cfcase>
                    <cfcase value="Validation">
                    
                         <cfoutput>
                         
                              <p>#FORM.ValidationHeader#</p>
                              
                              <p>Please click the button <span
                              style="background-color:##FFFF00">below</span>
                              to return to the preceeding page and correct
                              the following error(s).  
                              <span style="color:##FF0000; font-style:italic">(The
                              browser's back button will not work properly
                              on this page.)</span></p>
                              
                              <p>INVALID FIELD(S): #FORM.InvalidFields#</p>
                         
                         </cfoutput>
                         
                         <P><FORM>
                              <INPUT type="button"
                                   value="Please Click Here To Go Back"
                                   onClick="history.go(-2)" />
                         </FORM></p>
                    
                    </cfcase>
                    <cfdefaultcase>
                    
                         <p>The application is missing some necessary data.</p>
                         
                         <p>Did you try to access this web page directly
                         (I.E. did you type in the name of this file ---
                         <em><tt>http<cfif CGI.HTTPS EQ
                         "on">s</cfif>://<cfoutput>#CGI.SERVER_NAME##CGI.PATH_INFO#<cfif
                         isDefined("CGI.QUERY_STRING") AND
                         CGI.QUERY_STRING NEQ
                          ''>?#CGI.QUERY_STRING#</cfif></cfoutput></tt></em>
                         --- directly into the address bar?   Did you follow
                         a bookmark or favorite to get here?  
                         This web page only works properly when called by an
                         error.</p>
                    
                    </cfdefaultcase>
               
               </cfswitch>
          
          </TD></TR>
     </TABLE>

</div>

</body>
</html>

 

Reducing Redundant Markup

Now we have two templates --- errException.cfm and the fourth template --- where we have customized the markup to match the appearance of the web site.   But is that necessary?   Really, all we have to do is customize the appearance of the fourth template and make sure all errors are displayed through that template (including the exception errors.)

Revised Exception Error Template

<!---
Exception Error Template: This template can contain any CF functions, variables, or tags --- including ERROR scope variables and CFMAIL
--->

<!---
The CFPARAM tags are mostly for debugging this template.  While CFERROR is on, errors in this template will lead to an infinite loop as the template call itself.  Naturally it's best to test this template directly before making it live.  To do so, first comment out the CFERROR tag in Application.cfm and then visit this page by typing the URL in the address bar. Any errors in the template should generate an error message --- one which does not call this template.
--->
<!--- NOTE: ERROR.Mailto is defined in application.cfm --->
<cfparam name="ERROR.MailTo" default="webmaster@MyDomain.com">
<cfparam name="ERROR.DateTime" default="">
<cfparam name="ERROR.Message" default="">
<cfparam name="ERROR.RemoteAddress" default="">
<cfparam name="ERROR.Browser" default="">
<cfparam name="ERROR.HTTPReferer" default="">
<cfparam name="ERROR.Template" default="">
<cfparam name="ERROR.QueryString" default="">
<cfparam name="ERROR.Diagnostics" default="">

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<HEAD>
     <TITLE>Exception Error!</TITLE>
     <link href="main.css" rel="stylesheet" type="text/css" />
</HEAD>
<!--- Submit the form immediately and automatically --->
<BODY onload="document.goform.submit()">

<!--- Pass scoped variables to the next template via a form --->
<form id="goform" name="goform" method="post" action="actError.cfm">
     <input type="hidden" name="ErrorType" value="Exception" />
     <cfoutput>
          <input type="hidden" name="MailTo" value="#ERROR.MailTo#" />
          <input type="hidden" name="Datetime" value="#ERROR.Datetime#" />
          <input type="hidden" name="Message" value="#ERROR.Message#" />
          <input type="hidden" name="RemoteAddress" value="#ERROR.RemoteAddress#" />
          <input type="hidden" name="Browser" value="#ERROR.Browser#" />
          <input type="hidden" name="HTTPReferer" value="#ERROR.HTTPReferer#" />
          <input type="hidden" name="Template" value="#ERROR.Template#" />
          <input type="hidden" name="QueryString" value="#ERROR.QueryString#" />
          <input type="hidden" name="RemoteAddress" value="#ERROR.RemoteAddress#" />
          <input type="hidden" name="Browser" value="#ERROR.Browser#" />
          <input type="hidden" name="Diagnostics" value="#ERROR.Diagnostics#" />
     </cfoutput>
</form>

</BODY>
</HTML>

 

Revised Fourth Template: dspCustomError.cfm

<!---
dspCustomError.cfm for Exception, Request, and Validation errors.  This template takes error information from errException.cfm, err_request.cfm, or err_validation.cfm, emails the error information to the developer, and displays the error message to the user.
--->

<cfparam name="URL.action" default="Unknown">
<cfoutput><cfparam name="LOCAL.action" default="#URL.action#"></cfoutput>
<cfparam name="LOCAL.action" type="regex" pattern="[A-Za-z0-9_]{3,30}">
<cfparam name="FORM.ErrorType" default="Unknown">

<!---
1. In case this template is accessed directly through a favorite, bookmark, etc., establish a default email address from which to send emails.
--->
<cfparam name="SESSION.UserEmail" type="email" default="helpdesk@MyDomain.com">

<!---
2. Send email to developer with information about the error (Exception, Request, or Validation error.)
--->

<cfswitch expression="#FORM.ErrorType#">

     <cfcase value="Exception">
     
          <!--- CFPARAMs for debugging or hacking attempts --->
          <cfparam name="FORM.QUERYSTRING" default="">
          <cfparam name="FORM.TEMPLATE" default="">
          <cfparam name="ERROR.Mailto" type="email"
               default="Webmaster@MyDomain.com">
          <cfoutput>
               <cfparam name="FORM.MailTo" type="email"
                    default="#ERROR.Mailto#">
          </cfoutput>
          <cfparam name="FORM.REMOTEADDRESS" default="">
          <cfparam name="FORM.MESSAGE" default="">
          <cfparam name="FORM.BROWSER" default="">
          <cfparam name="FORM.DATETIME" default="">
          <cfparam name="FORM.DIAGNOSTICS" default="">
          <cfparam name="FORM.HTTPReferer" default="">

          <!--- Notify the developer of the exception error via email --->
          <cfif isDefined("FORM.Mailto") AND FORM.MailTo NEQ "" AND
               SESSION.UserEmail NEQ "">
               
               <cfmail to="#FORM.MailTo#" from="#SESSION.UserEmail#"
                    subject="Exception Error" type="html">
               
                    <p>User: #CGI.REMOTE_USER#</p>
                    <p>ERROR DATE and TIME:
                         <cfswitch
                              expression="#DayofWeek(FORM.Datetime)#">
                                   <cfcase value="1">Sun</cfcase>
                                   <cfcase value="2">Mon</cfcase>
                                   <cfcase value="3">Tue</cfcase>
                                   <cfcase value="4">Wed</cfcase>
                                   <cfcase value="5">Thu</cfcase>
                                   <cfcase value="6">Fri</cfcase>
                                   <cfcase value="7">Sat</cfcase>
                         </cfswitch>
                         #DateFormat(FORM.Datetime,"yyyy-mm-dd")#
                         #TimeFormat(FORM.Datetime, "HH:MM")#
                         CST</p>
                    <p>EMAIL DATE and TIME:
                         <cfswitch
                              expression="#DayofWeek(NOW())#">
                              <cfcase value="1">Sun</cfcase>
                              <cfcase value="2">Mon</cfcase>
                              <cfcase value="3">Tue</cfcase>
                              <cfcase value="4">Wed</cfcase>
                              <cfcase value="5">Thu</cfcase>
                              <cfcase value="6">Fri</cfcase>
                              <cfcase value="7">Sat</cfcase>
                         </cfswitch>
                         #DateFormat(NOW(),"yyyy-mm-dd")#
                         #TimeFormat(NOW(), "HH:MM")#
                         CST</p>
                    <p>IP Address: #FORM.REMOTEADDRESS#</p>
                    <p>User’s Browser: #FORM.Browser#</p>
                    <p>Referring Page: #FORM.HTTPReferer#</p>
                    <p>Error Page: [SNIP]</p>
                    <p>------------------------------------</p>
                    <p>#FORM.Message#</p>
                    <p>#FORM.DIAGNOSTICS#</p>
               
               </cfmail>
          
          </cfif>
     
     </cfcase>
     <cfcase value="Request">
     
          <cfparam name="FORM.HTTPReferer" default="">
          <cfparam name="FORM.Template" default="">
          <cfparam name="FORM.QueryString" default="">
          <cfparam name="FORM.Datetime" default="">
          <cfparam name="FORM.RemoteAddress" default="">
          <cfparam name="FORM.Browser" default="">
          <cfparam name="FORM.Diagnostics" default="">

          <cfparam name="FORM.MailTo" default="Webmaster@MyDomain.com">
          <cfparam name="FORM.MailTo" type="email">
          
          <!--- NOTE: "Portal" is the value for PortalDataSourceName --->
          
          <!--- Notify the developer of the exception error via email --->
          <cfif FORM.MailTo NEQ "" AND SESSION.UserEmail NEQ "">
          
               <cfmail to="#FORM.MailTo#" from="#SESSION.UserEmail#"
                    subject="Request Error" type="html">
               
                    <p>#FORM.ErrorType# Error</p>
                    <p>User: #CGI.REMOTE_USER#</p>
                    <p>ERROR DATE and TIME:
                    <cfswitch
                         expression="#DayofWeek(FORM.Datetime)#">
                         <cfcase value="1">Sun</cfcase>
                         <cfcase value="2">Mon</cfcase>
                         <cfcase value="3">Tue</cfcase>
                         <cfcase value="4">Wed</cfcase>
                         <cfcase value="5">Thu</cfcase>
                         <cfcase value="6">Fri</cfcase>
                         <cfcase value="7">Sat</cfcase>
                    </cfswitch>
                    #DateFormat(FORM.Datetime,"yyyy-mm-dd")#
                    #TimeFormat(FORM.Datetime, "HH:MM")# CST</p>
                    <p>EMAIL DATE and TIME:
                         <cfswitch
                              expression="#DayofWeek(NOW())#">
                              <cfcase value="1">Sun</cfcase>
                              <cfcase value="2">Mon</cfcase>
                              <cfcase value="3">Tue</cfcase>
                              <cfcase value="4">Wed</cfcase>
                              <cfcase value="5">Thu</cfcase>
                              <cfcase value="6">Fri</cfcase>
                              <cfcase value="7">Sat</cfcase>
                         </cfswitch>
                         #DateFormat(NOW(),"yyyy-mm-dd")#
                         #TimeFormat(NOW(), "HH:MM")# CST</p>
                    <p>REFERRING PAGE: #FORM.HTTPReferer#</p>
                    <p>Error Page: [SNIP]</p>
                    <p>IP NUMBER: #FORM.RemoteAddress#</p>
                    <p>BROWSER: #FORM.Browser#</p>
                    <p>-------- BEGIN DIAGNOSTICS ---------</p>
                    <p>Diagnostics: #FORM.Diagnostics#</p>
                    <p>--------- END DIAGNOSTICS ----------</p>
               
               </cfmail>
               
          </cfif>
     
     </cfcase>
     <cfcase value="Validation">
     
          <cfparam name="FORM.ValidationHeader" default="">
          <cfparam name="FORM.InvalidFields" default="">
          <cfparam name="FORM.ValidationFooter" default="">

          <!---
          Note: Validation errors do NOT use
          the ERROR.MailTo variable.
          --->
          <cfif EmailSendTo NEQ "" AND SESSION.UserEmail NEQ "">
          
               <cfmail to="#EmailSendTo#" from="#SESSION.UserEmail#"
                    subject="Validation Error" type="html">
                    
                    <p>Validation Error(s)</p>
                    <p>User: #CGI.REMOTE_USER#</p>
                    <p>DATE and TIME:
                         <cfswitch
                              expression="#DayofWeek(NOW())#">
                              <cfcase value="1">Sun</cfcase>
                              <cfcase value="2">Mon</cfcase>
                              <cfcase value="3">Tue</cfcase>
                              <cfcase value="4">Wed</cfcase>
                              <cfcase value="5">Thu</cfcase>
                              <cfcase value="6">Fri</cfcase>
                              <cfcase value="7">Sat</cfcase>
                         </cfswitch>
                         #DateFormat(NOW(),"yyyy-mm-dd")#
                         #TimeFormat(NOW(), "HH:MM")#
                         CST</p>
                    <p>DATE and TIME: #dateformat(NOW(),"yyyy-mm-dd")#
                         #timeformat(now(), "HH:MM")#</p>
                    <p>EMAIL DATE and TIME:
                    #dateformat(NOW(),"yyyy-mm-dd")#
                    #timeformat(now(), "HH:MM")#</p>
                    <p>USER IP ADDRESS: #CGI.REMOTE_ADDR#</p>
                    <p>USER BROWSER: #CGI.HTTP_USER_AGENT#</p>
                    <p>URL: #FORM.HTTPReferer#</p>
                    <p>------------------------------------</p>
                    <p>Invalid Fields: #FORM.InvalidFields#</p>
          
               </cfmail>
          
          </cfif>
     
     </cfcase>

</cfswitch>

<!---
3. Present error msg. to user: either Exception, Request, or Validation error
--->
<h1 style="text-align:center"><cfoutput>#FORM.ErrorType#</cfoutput> Error</h1>

<div align="center">

<table class="err_table">
     <caption>An error has occurred that prevents us from completing your request.</caption>
     <TR><TD>

     <cfswitch expression="#FORM.ErrorType#">

          <cfcase value="Exception">
     
               <cfoutput>
               
                    <p>Please cut and paste this error message
                    into an email and provide us with any
                    additional information that would help
                    us fix this problem (E.G. what you were
                    trying to do when the error occured, any
                    data you included in a form, etc.,)  
                    Send the email to
                    <A HREF="mailto:#FORM.MailTo#?subject=Exception
                    Error">#FORM.MailTo#</A>.</p>
                    
                    <p>ERROR MESSAGE: #FORM.Message#</p>
                    
                    <p>URL: [SNIP]</p>
                    
                    <p>DATE and TIME:
                    <cfswitch
                         expression="#DayofWeek(NOW())#">
                         <cfcase value="1">Sun</cfcase>
                         <cfcase value="2">Mon</cfcase>
                         <cfcase value="3">Tue</cfcase>
                         <cfcase value="4">Wed</cfcase>
                         <cfcase value="5">Thu</cfcase>
                         <cfcase value="6">Fri</cfcase>
                         <cfcase value="7">Sat</cfcase>
                    </cfswitch>
                    #dateformat(NOW(),"yyyy-mm-dd")#
                    #timeformat(now(), "HH:MM")#
                    CST</p>
               
               </cfoutput>
               
               <p><FORM>
               <INPUT type="button"
                    value="Please Click Here To Go Back"
                    onClick="history.go(-2)" />
               </FORM> </p>
          
          </cfcase>
          <cfcase value="Request">
          
               <cfoutput>
               
                    <p>Please cut and paste this error message into
                    an email and provide us with any additional
                    information that would help us fix this
                    problem (E.G. what you were trying to do when
                    the error occured, any data you included in
                    a form, etc.,)   Send the email to
                    <A HREF="mailto:#FORM.MailTo#?subject=Request
                    Error">#FORM.MailTo#</A>.</p>
                    
                    <p>DATE and TIME:
                    <cfswitch
                         expression="#DayofWeek(NOW())#">
                         <cfcase value="1">Sun</cfcase>
                         <cfcase value="2">Mon</cfcase>
                         <cfcase value="3">Tue</cfcase>
                         <cfcase value="4">Wed</cfcase>
                         <cfcase value="5">Thu</cfcase>
                         <cfcase value="6">Fri</cfcase>
                         <cfcase value="7">Sat</cfcase>
                    </cfswitch>
                    #DateFormat(NOW(),"yyyy-mm-dd")#
                    #TimeFormat(NOW(), "HH:MM")#
                    CST</p></p>
                    <p>REFERRING PAGE:
                    #FORM.HTTPReferer#</p>
                    <p>------------------------------</p>
                    <p>ERROR PAGE: [SNIP]</p>
                    <p>Diagnostics: #FORM.Diagnostics#</p>
               
               </cfoutput>
               
          </cfcase>
          <cfcase value="Validation">
               
               <cfoutput>
               
                    <p>#FORM.ValidationHeader#</p>
                    
                    <p>Please click the button <span
                    style="background-color:
                    ##FFFF00">below</span> to
                    return to the preceding page
                    and correct the following error(s).
                     <span style="color:##FF0000;
                    font-style: italic">(The browser's
                    back button will not work
                    properly on this page.)</span></p>
                    
                    <p>INVALID FIELD(S):
                    #FORM.InvalidFields#</p>
                    
                    <p><FORM>
                    <INPUT type="button"
                         value="Please Click Here To Go Back"
                         onClick="history.go(-2)" />
                    </FORM> </p>
               
               </cfoutput>
          
          </cfcase>
          <cfdefaultcase>
          
               <p>The application did not receive all the
               data it requires.<br />
               <br />
               Did you try to access this web page directly
               into the address bar?   Did you
               follow a bookmark or favorite to get here?
                
               This web page only works properly when called
               by an error.</p>
          
          </cfdefaultcase>
     
     </cfswitch>

     </TD></TR>
</TABLE>

</div>

 

New Problem: Reloading the Error Display Screen

But there is a potential problem with this fourth template:   A user might reload the page.   What would happen then?

The problem now is that the email function --- as well as all the other code in the form --- will be performed every time the user reloads the page.   The developer will get an identical email message every time the user reloads the screen.

One solution is to split the fourth template into two new templates. One template will send an email to the developer with details about the error and then pass the data on. The other template will display the error message to the user.

If the user reloads the screen with the error message all that will happen is that the error message will refresh.   No other processing will occur.   The developer will get no additional email.

actError.cfm: Error Email Template

<!---
actError.cfm for Exception, Request, and Validation errors.  This template takes error information passed from other templates, emails the error information to the developer, and passes the information to dspError.cfm where an error message is displayed to the user.
--->

<cfparam name="URL.action" default="Unknown">
<cfoutput><cfparam name="LOCAL.action" default="#URL.action#"></cfoutput>
<cfparam name="LOCAL.action" type="regex" pattern="[A-Za-z0-9_]{3,30}">
<cfparam name="FORM.ErrorType" default="Unknown">
<cfparam name="FORM.Template" default="">

<!---
1. In case this template is accessed directly through a favorite, bookmark, etc., establish a default email address from which to send emails.
--->
<cfparam name="FORM.UserEmail" type="email" default="helpdesk@MyDomain.com">

<!---
2. Send email to developer with information about the error (Exception, Request, or Validation error.)
--->
<cfswitch expression="#FORM.ErrorType#">

     <cfcase value="Exception">
     
          <!--- Creates a variable for the URL with the error by combining FORM
          and CGI variables --->
          <cfparam name="FORM.ErrorPage" default="">
          <cfif CGI.HTTPS EQ "off">
               <cfset FORM.ErrorPage = "https://" &
                    CGI.server_name & FORM.Template>
          <cfelse>
               <cfset FORM.ErrorPage = "https://" &
                    CGI.server_name & FORM.Template>
          </cfif>
          
          <cfif isDefined("FORM.QueryString") AND FORM.QueryString NEQ ''>
               <cfset FORM.ErrorPage = FORM.ErrorPage & "?" & FORM.QueryString>
          </cfif>
          
          <!--- CFPARAMs for debugging or when users try to access this template
          directly. --->
          <cfparam name="FORM.Mailto" type="email"
               default="webmaster@MyDomain.com">
          <cfoutput>
               <cfparam name="FORM.MailTo" type="email"
                 default="#FORM.Mailto#">
          </cfoutput>
          <cfparam name="FORM.REMOTEADDRESS" default="">
          <cfparam name="FORM.MESSAGE" default="">
          <cfparam name="FORM.BROWSER" default="">
          <cfparam name="FORM.DATETIME" default="">
          <cfparam name="FORM.DIAGNOSTICS" default="">
          <cfparam name="FORM.HTTPReferer" default="">
          
          <!--- Notify the developer of the exception error via email --->
          <cfif isDefined("FORM.Mailto") AND FORM.MailTo NEQ "" AND
               FORM.UserEmail NEQ "">
               
               <cfmail to="#FORM.MailTo#" from="#FORM.UserEmail#"
                    subject="Exception Error" type="html">
                    
                    <p>User: #CGI.REMOTE_USER#</p>
                    <p>ERROR DATE and TIME:
                    <cfswitch
                         expression="#DayofWeek(FORM.Datetime)#">
                         <cfcase value="1">Sun</cfcase>
                         <cfcase value="2">Mon</cfcase>
                         <cfcase value="3">Tue</cfcase>
                         <cfcase value="4">Wed</cfcase>
                         <cfcase value="5">Thu</cfcase>
                         <cfcase value="6">Fri</cfcase>
                         <cfcase value="7">Sat</cfcase>
                    </cfswitch>
                    #DateFormat(FORM.Datetime,"yyyy-mm-dd")#
                    #TimeFormat(FORM.Datetime, "HH:MM")#
                    CST</p>
                    <p>EMAIL DATE and TIME:
                    <cfswitch
                         expression="#DayofWeek(NOW())#">
                         <cfcase value="1">Sun</cfcase>
                         <cfcase value="2">Mon</cfcase>
                         <cfcase value="3">Tue</cfcase>
                         <cfcase value="4">Wed</cfcase>
                         <cfcase value="5">Thu</cfcase>
                         <cfcase value="6">Fri</cfcase>
                         <cfcase value="7">Sat</cfcase>
                    </cfswitch>
                    #DateFormat(NOW(),"yyyy-mm-dd")#
                    #TimeFormat(NOW(), "HH:MM")#
                    CST</p>
                    <p>IP Address: #FORM.REMOTEADDRESS#</p>
                    <p>User’s Browser: #FORM.Browser#</p>
                    <p>Referring Page: #FORM.HTTPReferer#</p>
                    <p>-------- BEGIN DIAGNOSTICS ---------</p>
                    <p>Error Page: #FORM.ErrorPage#</p>
                    <p>#FORM.Message#</p>
                    <p>#FORM.DIAGNOSTICS#</p>
                    <p>--------- END DIAGNOSTICS ----------</p>
               
               </cfmail>
          
          </cfif>
     
     </cfcase>
     <cfcase value="Request">
     
          <!--- Creates a variable for the FORM.ErrorPage (URL with the error)
          by combining FORM and CGI variables --->
          <cfparam name="FORM.ErrorPage" default="">
          
          <cfif CGI.HTTPS EQ "off">
               <cfset FORM.ErrorPage = "https://"
                    & CGI.server_name & FORM.Template>
          <cfelse>
               <cfset FORM.ErrorPage = "https://"
                    & CGI.server_name & FORM.Template>
          </cfif>
          
          <cfif isDefined("FORM.QueryString") AND FORM.QueryString NEQ ''>
               <cfset FORM.ErrorPage = FORM.ErrorPage & "?" &
                    FORM.QueryString>
          </cfif>
          
          <!--- Displays all FORM scope variables --->
          <cfif isDefined("FORM.FieldNames")>
               <div align="center">
                    <TABLE class="err_table">
                         <TR>
                              <TH>Variable Name</TH>
                              <TH>Value</TH>
                         </TR>
                         <!--- loop over the Form structure and
                         output all of the variable names
                         and their associated values --->
                         <CFLOOP COLLECTION="#Form#" ITEM="VarName">
                              <CFOUTPUT>
                                   <TR>
                                        <TD>#VarName#   </TD>
                                        <TD>#Form[VarName]#  </TD>
                                   </TR>
                              </CFOUTPUT>
                         </CFLOOP>
                    </TABLE>
               </div>
          </cfif>
          
          <cfparam name="FORM.Datetime" default="">
          <cfparam name="FORM.RemoteAddress" default="">
          <cfparam name="FORM.Browser" default="">
          <cfparam name="FORM.Diagnostics" default="">
          <!--- <cfparam name="FORM.generatedContent" default=""> --->
          <cfparam name="FORM.MailTo" default="webmaster@MyDomain.com">
          <cfparam name="FORM.MailTo" type="email">
          
          <!--- Notify the developer of the request error via email --->
          <cfif FORM.MailTo NEQ "" OR FORM.UserEmail NEQ "">
          
               <cfmail to="#FORM.MailTo#" from="#FORM.UserEmail#"
                    subject="Request Error" type="html">
                    
                    <p>#FORM.ErrorType# Error</p>
                    <p>USER: #CGI.REMOTE_USER#</p>
                    <p>ERROR DATE and TIME:
                         <cfswitch
                              expression="#DayofWeek(FORM.Datetime)#">
                              <cfcase value="1">Sun</cfcase>
                              <cfcase value="2">Mon</cfcase>
                              <cfcase value="3">Tue</cfcase>
                              <cfcase value="4">Wed</cfcase>
                              <cfcase value="5">Thu</cfcase>
                              <cfcase value="6">Fri</cfcase>
                              <cfcase value="7">Sat</cfcase>
                         </cfswitch>
                         #DateFormat(FORM.Datetime,"yyyy-mm-dd")#
                         #TimeFormat(FORM.Datetime, "HH:MM")#
                         CST</p>
                    <p>EMAIL DATE and TIME:
                         <cfswitch expression="#DayofWeek(NOW())#">
                              <cfcase value="1">Sun</cfcase>
                              <cfcase value="2">Mon</cfcase>
                              <cfcase value="3">Tue</cfcase>
                              <cfcase value="4">Wed</cfcase>
                              <cfcase value="5">Thu</cfcase>
                              <cfcase value="6">Fri</cfcase>
                              <cfcase value="7">Sat</cfcase>
                         </cfswitch>
                         #DateFormat(NOW(),"yyyy-mm-dd")#
                         #TimeFormat(NOW(), "HH:MM")#
                         CST</p>
                    <p>REFERRING PAGE: #FORM.HTTPReferer#</p>
                    <p>IP NUMBER: #FORM.RemoteAddress#</p>
                    <p>BROWSER: #FORM.Browser#</p>
                    <p>-------- BEGIN DIAGNOSTICS ---------</p>
                    <p>ERROR PAGE: #FORM.ErrorPage#</p>
                    <p>Diagnostics: #FORM.Diagnostics#</p>
                    <p>--------- END DIAGNOSTICS ----------</p>
               
               </cfmail>
          
          </cfif>
     
     </cfcase>
     <cfcase value="Validation">
     
          <cfparam name="FORM.ValidationHeader" default="">
          <cfparam name="FORM.InvalidFields" default="">
          <cfparam name="FORM.ValidationFooter" default="">
          
          <!--- Notify the developer of the request error via email --->
          <cfif FORM.MailTo NEQ "" OR FORM.UserEmail NEQ "">
          
               <!--- Validation errors do not use the ERROR.MailTo variable. --->
               <cfmail to="#SESSION.MailTo#" from="#FORM.UserEmail#"
                    subject="Validation Error" type="html">
               
                    <p>Validation Error(s)</p>
                    <p>USER: #CGI.REMOTE_USER#</p>
                    <p>DATE and TIME:
                    <cfswitch expression="#DayofWeek(NOW())#">
                         <cfcase value="1">Sun</cfcase>
                         <cfcase value="2">Mon</cfcase>
                         <cfcase value="3">Tue</cfcase>
                         <cfcase value="4">Wed</cfcase>
                         <cfcase value="5">Thu</cfcase>
                         <cfcase value="6">Fri</cfcase>
                         <cfcase value="7">Sat</cfcase>
                    </cfswitch>
                    #DateFormat(NOW(),"yyyy-mm-dd")#
                    #TimeFormat(NOW(), "HH:MM")#
                    CST</p>
                    <p>DATE and TIME: #dateformat(NOW(),"yyyy-mm-dd")#
                    #timeformat(now(), "HH:MM")#</p>
                    <p>EMAIL DATE and TIME:
                    #dateformat(NOW(),"yyyy-mm-dd")#
                    #timeformat(now(), "HH:MM")#</p>
                    <p>USER IP ADDRESS: #CGI.REMOTE_ADDR#</p>
                    <p>USER BROWSER: #CGI.HTTP_USER_AGENT#</p>
                    <p>URL: #FORM.ReferringPage#</p>
                    <p>----------- BEGIN ERRORS -----------</p>
                    <p>Invalid Fields: #FORM.InvalidFields#</p>
                    <p>------------ END ERRORS ------------</p>
               
               </cfmail>
          
          </cfif>
     
     </cfcase>

</cfswitch>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<HEAD>
     <TITLE>Error Action Page</TITLE>
     <link href="main.css" rel="stylesheet" type="text/css" />
</HEAD>
<BODY onload="document.goform.submit()"><!--- Submit the form immediately and automatically --->

<!--- 4. Pass the FORM variables to the dspError.cfm template which displays the error message to the user.--->
<FORM id="goform" name="goform" action="dspCustomError.cfm" method="post">
     <cfoutput>
          <cfloop list="#form.fieldnames#" index="thisfield">
               <input type="hidden" name="#thisfield#" value="#Trim(evaluate(thisfield))#" /><br />
          </cfloop>
     </cfoutput>
     <!--- <input type="submit" name="Submit" value=" Continue " /> --->
</FORM>

</BODY>
</HTML>

 

dspError.cfm: Error Display Template

<!---
Gets info from actError.cfm. Present error message to user: either Exception, Request, or Validation error
--->

<h1 style="text-align:center"><cfoutput>#FORM.ErrorType#</cfoutput> Error</h1>

<div align="center">

        <TABLE class="err_table">
                <caption>An error has occurred that prevents us from completing your request.</caption>
                <TR><TD style="text-align:left">
                
                        <cfswitch expression="#FORM.ErrorType#">
                        
                                <cfcase value="Exception">
                                
                                        <cfoutput>
                                        
                                                <p>Please cut and paste this error
                                                message into an email and provide us
                                                with any additional information that
                                                would help us fix this problem (E.G.
                                                what you were trying to do when the
                                                error occured, any data you included
                                                in a form, etc.,)   Send the email to <A
                                                HREF="mailto:#FORM.MailTo#?subject=Exception
                                                Error">#FORM.MailTo#</A>.</p>
                                                
                                                <p>ERROR MESSAGE: #FORM.Message#</p>
                                                
                                                <p>URL: [SNIP]</p>
                                                
                                                <p>DATE and TIME:
                                                <cfswitch
                                                        expression="#DayofWeek(NOW())#">
                                                        <cfcase value="1">Sun</cfcase>
                                                        <cfcase value="2">Mon</cfcase>
                                                        <cfcase value="3">Tue</cfcase>
                                                        <cfcase value="4">Wed</cfcase>
                                                        <cfcase value="5">Thu</cfcase>
                                                        <cfcase value="6">Fri</cfcase>
                                                        <cfcase value="7">Sat</cfcase>
                                                </cfswitch>
                                                #dateformat(NOW(),"yyyy-mm-dd")#
                                                #timeformat(now(), "HH:MM")#
                                                CST</p>
                                        
                                        </cfoutput>
                                
                                <p><FORM>
                                <INPUT type="button"
                                value="Please Click Here To Go Back"
                                onClick="history.go(-3)" />
                                </FORM></p>
                                
                                </cfcase>
                                <cfcase value="Request">
                                
                                        <p>Please click the button <span
                                        style="background-color:##FFFF00">below</span> to
                                        return to the preceeding page and correct the
                                        following error(s).
                                        <span style="color:##FF0000;
                                        font-style:italic">(The browser's back button
                                        will not work properly on this page.)</span></p>
                                        
                                        <cfoutput>
                                        
                                                <p>Please copy this error into an email
                                                and provide us with any additional
                                                information that would help us fix this
                                                problem (E.G. what you were trying to do
                                                when the error occured, any data you
                                                included in a form, etc.,)   Send the
                                                email to <A
                                                HREF="mailto:#FORM.MailTo#?subject=Request
                                                Error">#FORM.MailTo#</A>.</p>
                                                
                                                <p>DATE and TIME:
                                                <cfswitch
                                                        expression="#DayofWeek(NOW())#">
                                                        <cfcase value="1">Sun</cfcase>
                                                        <cfcase value="2">Mon</cfcase>
                                                        <cfcase value="3">Tue</cfcase>
                                                        <cfcase value="4">Wed</cfcase>
                                                        <cfcase value="5">Thu</cfcase>
                                                        <cfcase value="6">Fri</cfcase>
                                                        <cfcase value="7">Sat</cfcase>
                                                </cfswitch>
                                                #DateFormat(NOW(),"yyyy-mm-dd")#
                                                #TimeFormat(NOW(), "HH:MM")#
                                                CST</p>
                                                <p>REFERRING PAGE: #FORM.HTTPReferer#</p>
                                                <p>ERROR PAGE:[SNIP]</p>
                                                <p>DIAGNOSTICS: #FORM.Diagnostics#</p>
                                        
                                        </cfoutput>
                                        
                                        <p><FORM>
                                        <INPUT type="button"
                                        value="Please Click Here To Go Back"
                                        onClick="history.go(-3)" />
                                        </FORM></p>
                                
                                </cfcase>
                                <cfcase value="Validation">
                                
                                        <p><cfoutput>#FORM.ValidationHeader#
                                             </cfoutput></p>
                                        
                                        <p>Please click the button <span
                                        style="background-color:##FFFF00">below</span>
                                        
                                        to return to the preceeding page and correct the
                                        following error(s).   <span
                                        style="color:#FF0000; font-style:italic">(The
                                        browser's back button will not work properly
                                        on this page.)</span></p>
                                        
                                        <p>INVALID FIELD(S):
                                        <cfoutput>#FORM.InvalidFields#</cfoutput></p>
                                        
                                        <p><FORM>
                                        <INPUT type="button"
                                        value="Please Click Here To Go Back"
                                        onClick="history.go(-3)" />
                                        </FORM></p>
                                
                                </cfcase>
                                <cfdefaultcase>
                                
                                        <p>The application is missing some necessary
                                             data.</p>                                         
                                        <p>Did you try to access this web page directly
                                        (I.E. did you type in the name of this file
                                        directly into the address bar?   Did you
                                        follow a bookmark or favorite to get here?  
                                        This web page only works properly when called by
                                        an error.</p>
                                
                                </cfdefaultcase>
                        
                        </cfswitch>
                
                </TD></TR>
        </TABLE>

</div>

 

Fixing the Broken Back Link

Please note that using a interrupt / redirect in the way shown will disable the back button in your browser because the proceeding page will redirect you back to the error display page.   One solution to this problem is as follows.   In the Display template, include the following HTML in place of the "FORM.ValidationFooter" variable:

<FORM>
     <INPUT type="button" value="Please Click Here To Go Back"
          onClick="history.go(-3)">
</FORM>

(There are other methods of going back through your browsers history. I found this one to be the easiest for me.)

 

Error Tracking through a Database

It's possible to save the error incidents to a database by adding CFQUERY tag to the fourth template.   That way the developers can track their errors in a dashboard application.

 

Lagniappe: Adding Server Diagnostics

Adding the following bit of code to the fourth template isn't required but it can provide additional information to the developer.

<cfset pmData = GetMetricData("PERF_MONITOR")>
<cfoutput>
        <p>Current PerfMonitor data is: <br />
        InstanceName:   #pmData.InstanceName# <br />
        PageHits:       #pmData.PageHits# <br />
        ReqQueued:      #pmData.ReqQueued# <br />
        DBHits:         #pmData.DBHits# <br />
        ReqRunning:     #pmData.ReqRunning# <br />
        ReqTimedOut:    #pmData.ReqTimedOut# <br />
        BytesIn:        #pmData.BytesIn# <br />
        BytesOut:       #pmData.BytesOut# <br />
        AvgQueueTime:   #pmData.AvgQueueTime# <br />
        AvgReqTime:     #pmData.AvgReqTime# <br />
        AvgDBTime:      #pmData.AvgDBTime# </p>
</cfoutput>

 

Step Two: Debug the New Templates

Debug each template by adding CFPARAM tags and opening each template in a browser. (Doing this before adding the CFERROR tags will minimize cascading error messages in which the error template generates a vicious infinite loop.) Temporarily use CFPARAM tag to assign default values for all the individual values in the ERROR scope. (E.G.: <cfparam name="ERROR.ValidationHeader" default="">)

NOTE: This extra step will save you a lot of grief! Please DO IT! Once you're done, comment out or delete the CFPARAM tags in the request error and validation error templates. Because once you've followed the next step --- adding the CFERROR tags --- the application and the the CFPARAM tags won't play nice with each other.

 

Step Three: Add three CFERROR tags

None of these templates actually work yet.   We need to follow one more step:   Place the following tags in Application.cfm (or Application.cfc):

<CFERROR TYPE="REQUEST" TEMPLATE="errRequest.cfm" MAILTO="Webmaster@MyDomain.com">
<CFERROR TYPE="EXCEPTION" TEMPLATE="errException.cfm" MAILTO="Webmaster@MyDomain.com">
<CFERROR TYPE="VALIDATION" TEMPLATE="errValidation.cfm"
      MAILTO="Webmaster@MyDomain.com">

After this step, the templates will be called when you encounter errors. TIP: Add the Request tag first and debug that corresponding template before you add the other two CFERROR tags.

 

Steps Four and Five: Testing

Test the procedures on development and production. Intentionally create errors for each error type: exception, request, and validation. (It will be necessary to comment out the exception CFERROR to check the request error.)

<!---
Deliberately prompting an exception error by calling a variable that has not been declared
--->
<cfoutput>#UndefinedVariable#</cfoutput>

<!--- Deliberatley prompt a valiation error --->
<cfform action="home.cfm?action=validationerror" method="post">
      <cfinput type="hidden" name="integer" value="abc" validate="integer"
            message="Please enter an integer in the
            Integer field" validateat="onserver">
      <cfinput type="submit" value="Validation Error Msg." name="submit">
</cfform>

 

Extra Step for Fusebox

Add a "display error" fuseaction to home.cfm / main.cfm / index.cfm:

<cfcase value="dspError">
  <cfinclude template="dspError.cfm">
</cfcase>

Using fusebox, that's the extent of the customiztion. Assuming you have modified the Application.cfm file and included the other error files, it's not necessary modify any of the new files any further.

 

Another Option: Portal Wide Error Handling

In a portal environment where you have multiple applications and web sites in separate subdirectories sharing a common look and feel, it would make sense to have a single template for displaying a uniform error message.

Let's assume your portal is set up with directories like so:

C:\inetpub\wwwroot\portal
C:\inetpub\wwwroot\hr
C:\inetpub\wwwroot\sales
C:\inetpub\wwwroot\pr


With the first directory belonging to the "top tier" of your intranet or Internet presence and the other three directories belonging to individual sites belonging to the same domain. All the sites are owned and maintained by discrete organizational units but they all must maintain an identical look and feel.

This method makes that easy to do with the following steps:

  • Place dspError.cfm in the portal directory.

  • If you follow the Fusebox methodology, change the fuseaction in home.cfm / main.cfm / index.cfm in each seperate directory to the following:
    <cfcase value="dspError">
      <cfinclude template="../portal/dspError.cfm">
    </cfcase>


  • If you don't use the Fusebox methology, change the value for "action" in your actError.cfm template from dspError.cfm to ../portal/dspError.cfm.

Now every individual web site hosted on your web server shares a single template for displaying all of your exception, request, and validation errors.

 

Other Error Customization Techniques

The following techniques are outside the scope of this presentation but they are straight forward and covered in detail by readily available sources (such as Ben Forta's CFWACK series.)

  • Specifying template in IIS or Apache to catch web server errors such as 404 File Not Found

  • Specifying the missing template handler in ColdFusion Administrator.

  • Using client side and server side Form Data Validation with CFFORM and CFINPUT

  • Error Handling with CFTRY and CFCATCH

  • Custom coding your own functions with CFIF statements

  • Validating variables with CFPARAM

  • The IsValid Function: Function that performs data validation just like the CFPARAM tag and supports all the new data types in CFPARAM. (New with CF 7)
    <CFIF IsValid("integer", url.value)>
      Valid integer!
     <CFELSE>
      Invalid Integer!
    </CFIF>


 

In Conclusion

I hope you all got something useful out of this presentation. If you try out this solution and come up with improvements, corrections, or caveats, I'd love to hear about them.

Thank you!

 

top