There are multiple ways and techniques to program almost any task. Because of this, there is one thing I can guarantee: If you analyze how three different applications validate form data, you will find three different ways of doing it (assuming they validate at all). Some techniques are better than others. Here I'll describe one that works for me.

A little about me first so you understand where I am coming from. I have been a professional application designer and programmer for over 20 years; the last ten years I have had a heavy focus on web based applications using various technologies. I'll be the first to admit that my views of user applications is myopic -- I tend to see everything with a "how usable is the application to the user" and "how is the user going to break the application."

Now two of my pet peeves: 1) Programs that don't gracefully display entry errors to the users and 2) Programs that don't validate EVERYTHING that the user enters. To me, customer impression wise, there is nothing worse that a generic "Invalid data, press the back button and try again." System security wise, there is nothing worse that assuming that FORM data is valid and blindly acting on it. Also, client side validation (JavaScript, etc.) is good for "customer impression" but cannot be used as a substitute for server side validation.

Anyway, now that bored stiff with my high and mighty ideals, I'll present you with a simple technique I use for validating data.

The biggest difference between this technique and most "common" tutorials that I have seen on CF is that the FORM page and the ACTION page are one and the same and this has three key benefits that can be problematical using separate FORM and ACTION pages:

First, you have full control on what information is "defaulted" to the user in form fields and you can preserve any previously keyed information in the event of any entry errors. Second, with some very minor tweaks, you can allow the passing of "default" form field values via URL parameters. Third, using the <cflocation> tag to direct the user to the next page allows for refresh to work properly without the nagging "do you want to repost" browser message and all the associated code to handle double-posts.

MySampleForm.cfm:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<cfparam name="FORM.FirstName" default="">
<cfparam name="FORM.LastName" default="">
<cfparam name="FORM.CardNumber" default="">
<cfparam name="FORM.ExpirationMonth" default="">
<cfparam name="FORM.ExpirationYear" default="">
<cfparam name="FORM.Action" default="">

<cfset VARIABLES.y1=DatePart("yyyy",Now())>
<cfset VARIABLES.y2=VARIABLES.y1+10>

<cfparam name="URL.Msg" default="">
<cfset VARIABLES.Msg="#URL.Msg#">

<cfif VARIABLES.Msg is "" and FORM.Action is "Submit">
  <!-- validate data -->
  <cfif Trim(FORM.FirstName) is "">
    <cfset VARIABLES.Msg=VARIABLES.Msg & "<li>You must enter your first name</li>">
  </cfif>
  <cfif Trim(FORM.LastName) is "">
    <cfset VARIABLES.Msg=VARIABLES.Msg & "<li>You must enter your last name</li>">
  </cfif>
  <cfif Trim(FORM.CardNumber) is "">
    <!--
     This is a simple "card number entered" check for the
     purpose of this example; I would do a more thorough
     check to determine number was valid!
     -->
    <cfset VARIABLES.Msg=VARIABLES.Msg & "<li>You must enter your last name</li>">
  </cfif>
  <cfif Val(FORM.ExpirationMonth) LT 1 or Val(FORM.ExpirationMonth) GT 12>
    <cfset VARIABLES.Msg=VARIABLES.Msg & "<li>You must select a valid expiration month</li>">
  </cfif>
  <cfif Val(FORM.ExpirationYear) LT VARIABLES.y1 or Val(FORM.ExpirationYear) GT VARIABLES.y2>
    <cfset VARIABLES.Msg=VARIABLES.Msg & "<li>You must select a valid expiration year</li>">
  </cfif>
  <cfif VARIABLES.Msg is "">
    <!-- validation passed, process data -->
    <!?
          ADD YOUR CODE HERE TO PROCESS THE DATA, IF ANYTHING
          FAILS THEN SET VARIABLES.Msg
     -->
  <cfif VARIABLES.Msg is "">
    <!-- process completed successfully, send client to next page -->
    <!?
           MODIFY THE FOLLOWING CFLOACTION TAG TO SEND THE
           USER TO THE NEXT PAGE IN THE PROCESS LIKE A THANK
           YOU PAGE, YOU'VE BEEN VALIDATED, ETC.
    -->
    <cflocation url="MySampleForm.cfm?Msg=#URLEncodedFormat('Information validated & posted - Thank you!')#" addtoken="No">
  </cfif>
<cfelse>
  <!-- validation failed, make the message pretty for the user -->
  <cfset VARIABLES.Msg="<h1>ENTRY ERROR</h1>The following error(s) were detected:<ul>#VARIABLES.Msg#</ul>">
</cfif>
</cfif>

<html>
  <head>
    <title>My Test Form and Validation</title>
  </head>

  <body>
  <cfoutput>
  <cfif VARIABLES.Msg is not "">
    <p>#VARIABLES.Msg#</p>
  </cfif>
  <cfform action="MySampleForm.cfm" method="POST" enablecab="No">
  <table cellspacing="2" cellpadding="2" border="0">
    <tr>
      <th align="right">First Name</th>
      <td><cfinput type="Text" name="FirstName" value="#FORM.FirstName#" message="You must enter your first name." required="Yes" size="30"></td>
    </tr>
    <tr>
      <th align="right">Last Name</th>
      <td><cfinput type="Text" name="LastName" value="#FORM.LastName#" message="You must enter your last name." required="Yes" size="30"></td>
    </tr>
    <tr>
      <th align="right">Credit Card</th>
      <td><cfinput type="Text" name="CardNumber" value="#FORM.CardNumber#" message="You must enter your credit card number." validate="creditcard" required="Yes" size="20"></td>
   </tr>
   <tr>
      <th>Expiration</th>
      <td>
         <select name="ExpirationMonth">
           <cfloop index="i" from="1" to="12">
             <option value="#i#"<cfif FORM.ExpirationMonth EQ i> selected</cfif>>#NumberFormat(i,"00")#</option>
           </cfloop>
         </select>
         /
         <select name="ExpirationYear">
           <cfloop index="i" from="#VARIABLES.y1#" to="#VARIABLES.y2#">
              <option value="#i#"<cfif FORM.ExpirationYear EQ i> selected</cfif>>#NumberFormat(i,"0000")#</option>
           </cfloop>
         </select>
      </td>
    </tr>
    <tr>
      <td>&nbsp;</td>
      <td>
        <input type="submit" name="Action" id="Submit" value="Submit">
        <input type="reset" value="Reset">
      </td>
   </tr>
</table>
</cfform>
</cfoutput>
</body>
</html>

To summarize the technique:

  1. FORM page and ACTION page are one and the same (MySampleForm.cfm in this example)
  2. Define all your form variables using the <cfparam> tag -- this example default all the values to "" but you can default any value the application requires
  3. Make sure your submit button has a name and value and use this value to determine whether you are acting on POSTed data -- make sure the defaulted value for this field is "" (<cfparam name="FORM.Action" default="">)
  4. If acting on the data (FORM.Action is "Submit"), validate all the data -- I use VARIABLES.Msg to hold any entry or action error for displaying to the user
  5. If all the data validates, act on the data and if successful, use the <cflocation> tag to send the user to the next page in the process (this example sends us back to the same page with a message -- this is unlikely in a real application)
  6. If you fall through to the actual page building code, display any errors or warnings to the user, if applicable, and default the FORM fields to their prior or defaulted values

Security wise, step #4 is the most important step -- validate everything!

There are other advantages to this single page technique, especially if you have to deal sites that have secure and unsecure portions and you are attempting to secure as few pages as possible because of CPU and bandwidth limitations.

One last note, I mentioned an parameter passing tweak to allow passing "default" values as URL parameters ? here's the tweak:

<!-- original code from above -->
<cfparam name="FORM.FirstName" default="">
<cfparam name="FORM.LastName" default="">

<!-- the tweak -->
<cfparam name="URL.FirstName" default="">
<cfparam name="FORM.FirstName" default="#URL.FirstName#">
<cfparam name="URL.LastName" default="">
<cfparam name="FORM.LastName" default="#URL.LastName#">

Like I originally said, there are multiple ways program almost any task -- this is one way. Is it "the best?" -- I doubt it, but it's the best that I have found and it works for me. Hope this helps someone?
 

*** Seven Year Update - 10/06/2010 ***

It's been seven years since I originally posted this tutorial and my CF coding techniques have matured. While I still use the technique described in this tutorial, I have incorporated some of my Fusebox experience into applying this technique. I'll briefly describe the differences here but hopefully, if I get some time, I'll rewrite a 2010 version of the tutorial.

URL and FORM Scope

To make scope handling easier and consistent, I copy both the URL and FORM scopes into an ATTRIBUTES scope. Somewhere near the very tope of the template, I include the following:

<cfif NOT IsDefined("attributes")>
  <cfset attributes = StructNew() />
</cfif>
<cfif IsDefined("FORM")>
  <cfset tmp = StructAppend(attributes,FORM,false) />
</cfif>
<cfif IsDefined("URL")>
  <cfset tmp = StructAppend(attributes,URL,false) />
</cfif>

Now you only need a single cfparam for each variable you require as opposed to the cfparam of the URL variable and using it as the default for the cfparam of the FORM variable. Another advantage is that you can doctor the variable in the ATTRIBUTES scope all you want but if you need it, the original FORM or URL variable still exists with its original un-doctored value.

Fuses

Fusebox is build around fuses, or in non-fusebox terms, (usually) small modules which are easier to develop and maintain that single huge all encompassing mega modules. Even though the technique here does not require Fusebox, I still like the fuse concept.

In the above example, there is a somewhat large validation block:

<cfif VARIABLES.Msg is "" and FORM.Action is "Submit">
  <!-- validate data -->
  <cfif Trim(FORM.FirstName) is "">
    <cfset VARIABLES.Msg=VARIABLES.Msg & "<li>You must enter your first name</li>">
  </cfif>
  ...
</cfif>

Now a days, I take the contents of that entire block and put it in a separate module that gets cfincluded. This shortens this module (significantly in many cases) and makes it easier to maintain.

<cfif VARIABLES.Msg is "" and FORM.Action is "Submit">
  <cfinclude template="act_formValidate.cfm" />
</cfif>

Solid Coding

I forgot about this tutorial but when someone commented on it, I had to reread it.  I saw one part that made me cringe and I'm embarrassed that I included such a poor example in a tutorial designed for early ColdFusion programmers. As a challenge, go back and look at my coding example and see if you can find this glaring example of poor coding...

(LONG PAUSE FOR DRAMATIC EFFECT).....

If you did not find it yet, or you are unsure which atrocious code I am referring to, here is a hint: cross site scripting.

(slightly shorter pause)...

Got it? For programmers just starting out with ColdFusion, never -- I MEAN NEVER do something like this:

<cflocation url="MySampleForm.cfm?Msg=#URLEncodedFormat('Information validated & posted - Thank you!')#"  />

NEVER pass messages as-is in URL or FORM variables. This can lead to a bored degenerate crafting code to insult your customers with a message like "Thank you, now go jump in a lake!" to hackers doing whatever they want to you or your customers. Instead, something like this would have made a MUCH better example:

<cflocation url="MySampleForm.cfm?Msg=thanks" />

and change the initial Msg handling to this (using the attributes scope change):

<cfset variables.msg = "" />
<cfif attributes.msg EQ "thanks">
  <cfset variables.msg = "Information validated &amp;  posted - Thank you!"  />
</cfif>

Hope this update helps someone.

 

About This Tutorial
Author: Steve Sommers
Skill Level: Intermediate 
 
 
 
Platforms Tested: CF4,CF5,CFMX,CF8,CF9
Total Views: 51,328
Submission Date: January 09, 2011
Last Update Date: January 09, 2011
All Tutorials By This Autor: 1
Discuss This Tutorial
  • Great tutorial, but one this is odd to me. Perhaps it is a matter of what ColdFusion Server you are using (I am under CF 6.1) but when you specify the cfinput tag with a reuired field or another validation method it never goes to display the in-page error message - because it shows a javascript pop-up message (as defined in the cfiput) instead. I was wondering if you did this intentionally or has the implemntation of the cfform/cfinput changed since then? Thanks!

  • Thank you for the kind words. I wrote this tutorial three years ago and it seems like forever in my mind. In rereading it, I wish I had proofed the grammar better. Just to let you know, I still use this technique regularly. If you stick with ColdFusion, I urge you and anyone else reading this to look at FuseBox. It incorporates a technique similar to this but it gives you many additional advantages, some of the biggest being maintenance and supportability. Again, thank you and good luck.

  • I'm fairly new to all this (you've been programming almost as long as I've been alive) and I cringed when a client's host rejected PHP, but your tutorial helped me IMMENSELY and, believe me, I went through MANY before I found myself here. Thank you for posting :) -HB http://www.infornographer.com

  • You don't have to default the radio button but you still need to validate the value server side using something the following: ... I would also add a JavaScript check also just to give more immediate feedback if a value is not selected.

  • I have generated a form which works fine, but now at the point of determining, "how the user will break the form?" The majority of the areas in the form I have covered, except a set of three radio buttons. I could set a button and hope that the user looks at the buttons and sets the correct one for their circumstances, but I know Murphy will pop his head up and things will not go correctly. What I prefer is to set no radio buttons and if the user forgets to set one pop a message up telling them to select one. Only problem I am not smart enough to determine how to code that, or adapt some coding examples I have seen to my situation. Have any suggestions? Chuck

  • The example is more on how to handle data entry and data validation. What is done with the data is completely up to you. Simply insert you code to store the data in a database or send an e-mail in the block with the following comment:

  • All this I great. But where is the data going? Database, email? Can you enlighten is on this?

  • I'm sorry; I'm not sure what you mean by "not specifying that CFINPUT tag text need to be aligned right how to be mentioned"? If you are meaning that the TD tag for the CFINPUT do not include the ALIGN property, this was intentional. I prefer my headings to be as close to the input fields as possible while maintaining graphic appeal. If this is not what you meant, please ask again...

  • Hello you have mentioned all but you are not specifying that CFINPUT tag text need to be aligned right how to be mentioned venu

Advertisement

Sponsored By...
Powered By...