As mentioned earlier, server-side validation involves adding code to your application that performs form field validation after the form is submitted. In ColdFusion this usually is achieved with a series of <cfif> statements that check each field's value and data types. If any validation steps fail, processing can be terminated with the <cfabort> function, or the user can be redirected to another page (maybe the form itself) using <cflocation>.
The code shown in Listing 13.1 is a simple login prompt used to gain access to an intranet site. The file (which you should save as login1.cfm in a new directory named 13) prompts for a user ID and password. HTML's only validation rule, maxlength, is used in both form fields to restrict the number of characters that can be entered. The form itself is shown in Figure 13.1.
<!--- Name: login1.cfm Author: Ben Forta (ben@forta.com) Description: Basic server-side validation Created: 12/21/04 ---> &l275> <head> <title>Orange Whip Studios - Intranet</title> </head> <body> <!--- Page header ---> <cfinclude template="header.cfm"> <!--- Login form ---> <form action="process1.cfm" method="post"> <table align="center" bgcolor="orange"> <tr> <td align="right"> ID: </td> <td> <input type="text" name="LoginID" maxlength="5"> </td> </tr> <tr> <td align="right"> Password: </td> <td> <input type="password" name="LoginPassword" maxlength="20"> </td> </tr> <tr> <td colspan="2" align="center"> <input type="submit" value="Login"> </td> </tr> </table> </form> </body> <l>
This particular form gets submitted to a template named process1.cfm (specified in the action attribute). That template is responsible for validating the user input and processing the login only if all the validation rules passed. The validation rules necessary here are:
Login ID is required.
Login ID must be numeric.
Login password is required.
To perform this validation, three <cfif> statements are used, as shown in Listing 13.2.
<!--- Name: process1.cfm Author: Ben Forta (ben@forta.com) Description: Basic server-side validation Created: 12/21/04 ---> &l275> <head> <title>Orange Whip Studios - Intranet</title> </head> <body> <!--- Page header ---> <cfinclude template="header.cfm"> <!--- Make sure LoginID is not empty ---> <cfif Len(Trim(LoginID)) IS 0> <h1>ERROR! ID can't be left blank!</h1> <cfabort> </cfif> <!--- Make sure LoginID is a number ---> <cfif IsNumeric(LoginID) IS "No"> <h1>ERROR! Invalid ID specified!</h1> <cfabort> </cfif> <!--- Make sure LoginPassword is not empty ---> <cfif Len(Trim(LoginPassword)) IS 0> <h1>ERROR! Password can't be left blank!</h1> <cfabort> </cfif> <p align="center"> <h1>Intranet</h1> </p> Intranet would go here. </body> <l>
The first <cfif> checks the length of LoginID after trimming it with the trim() function. The trim() function is necessary to trap space characters that are technically valid characters in a text field but are not valid here. If the Len() function returns 0, an error message is displayed, and the <cfabort> statement halts further processing.
Checking the length of the trimmed string (to determine whether it's empty) is functionally the same as doing a comparison against an empty string, like this:
<cfif Trim(LoginID) IS ">
The reason I used Len() to get the string length (instead of comparing it to ") is that numeric comparisons are generally processed more quickly than string comparisons. For even greater performance, I could have eliminated the comparison value and used the following:
<cfif not Len(Trim(LoginID))>
The second <cfif> statement checks the data type. The IsNumeric() function returns trUE if the passed value was numeric (contained only digits, for example) or FALSE if not. Once again, if the <cfif> check fails, an error is displayed and <cfabort> halts further processing, as shown in Figure 13.2. The third <cfif> checks that a password was specified (and that that the field was not left blank).
Appendix C, "ColdFusion Function Reference," for a complete list of functions that can be used for form field validation. Most of the decision functions begin with is (for example, IsDefined() and IsDate()).
<cfif> statements can be combined using AND and OR operators if necessary. For example, the first two <cfif> statements shown in Listing 13.2 could be combined to read
<cfif (Len(Trim(LoginID)) IS 0) OR (NOT IsNumeric(LoginID))>
Of course, there is a downside to all of this. Managing and maintaining all of those <cfif> statements can get tedious and complex, especially since most of your forms will likely contain more than just two controls, as ours did here.
One solution to the proliferation of <cfif> statements in Chapter 9, "CFML Basics"). The <cfparam> tag has two distinct functions:
Providing default values for variables.
Performing field value validation.
The difference is whether or not a default is provided. Look at this example:
<cfparam name="LoginID">
No default value is provided, and so LoginID is required, and if not present an error will be thrown.
By contrast, this next example has a default value:
<cfparam name="color" default="red">
In this example color isn't required, and if not present, the default value of red will be used.
<cfparam> also supports one additional attribute, a type, as seen in this example:
<cfparam name="LoginID" type="integer">
In this example LoginID is required (because no default is specified). In addition, it must be an integer (a number), and if it's something other than an integer an error will be thrown. ColdFusion supports a complete range of validation types, as listed in Table 13.2.
TYPE
| DESCRIPTION
|
---|---|
any
| Allows any value
|
array
| A ColdFusion array
|
binary
| A binary value
|
boolean
| true (yes, true, or any non-zero number) or false (no, false, or 0)
|
creditcard
| A 13- or 16-digit credit card number that matches the MOD10 algorithm
|
date
| A date and time value (same as time)
|
| A well-formatted e-mail address
|
eurodate
| A date value in dd/mm/yy format
|
float
| A numeric value (same as numeric)
|
guid
| A UUID in the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
integer
| An integer value
|
numeric
| A numeric value (same as float)
|
query
| A ColdFusion query
|
range
| A range of numbers (range must be specified)
|
regex
| A regular expression pattern (same as regular_expression)
|
regular_expression
| A regular expression pattern (same as regex)
|
social_security_number
| A US format social security number (same as ssn)
|
ssn
| A U.S. format Social Security number (same as social_security_number)
|
string
| A string of one or more characters
|
struct
| A ColdFusion structure
|
telephone
| A US format phone number
|
time
| A date and time value (same as date)
|
url
| A file, ftp, http, https, mailto, or news url
|
usdate
| A date value in mm/dd/yy format
|
uuid
| A ColdFusion UUID in the form xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxx
|
variablename
| A string that meets ColdFusion variable naming rules
|
xml
| An XML object or string
|
zipcode
| A U.S. 5- or 5+4-digit ZIP code
|
Listing 13.3 is an updated version of Listing 13.2, this time replacing the <cfif> statements with <cfparam> tags.
<!--- Name: process2.cfm Author: Ben Forta (ben@forta.com) Description: <cfparam> server-side validation Created: 12/21/04 ---> <!--- Form field validation ---> <cfparam name="FORM.LoginID" type="integer"> <cfparam name="FORM.LoginPassword"> &l275> <head> <title>Orange Whip Studios - Intranet</title> </head> <body> <!--- Page header ---> <cfinclude template="header.cfm"> <p align="center"> <h1>Intranet</h1> </p> Intranet would go here. </body> <l>
The code in Listing 13.3 is much cleaner and simpler than the code in Listing 13.2., yet it accomplishes the same thing. To test this code, modify login1.cfm and change the <form> tag so that action="process2.cfm". TRy submitting the form with errors and you'll see a screen like the one shown in Figure 13.3
Chapter 19, "Introducing the Web Application Framework."
There is, however, a downside with both forms of server-side validation. If you were to add or rename a field, for example, you'd have to remember to update the destination page (the page to which the fields get submitted, as specified in the <form> action attribute), as well as the form itself. As your forms grow in complexity, so does the likelihood of your forms and their validation rules getting out of sync.
Server-side validation is the safest and most secure form of form field validation, but it can also become a maintenance niare. ColdFusion to the rescue!
<cfform> is ColdFusion's version of <form>, and <cfinput> is ColdFusion's version of <input>. The tags can be used interchangeably, and this code:
<form action="process.cfm" method="post"> <input type="text" name="search"> <input type="submit"> </form>
is functionally identical to:
<cfform action="process.cfm" method="post"> <cfinput type="text" name="search"> <cfinput type="submit" name="submit"> </cfform>
When ColdFusion processes the <cfform> tag it simply generates th260 <form> tag, and when it processes <cfinput> it generates <input>. So why bother doing this? Because these tags essentially intercept the form generation, allowing ColdFusion to insert other code as needed. For example, validation code. For example, look at the following code snippet:
<cfinput type="password" name="LoginPassword" maxlength="20" required="yes" message="Password is required!" validateAt="onServer">
This tag accepts a password, just like the <input> seen in Listing 13.1. But unlike the tag in that listing, here a <cfinput> tag is used. And once <input> has been replaced with <cfinput>, additional attributes (that are instructions to ColdFusion) may be introduced. required="yes" tells ColdFusion that the password field is required, message contains the error message to be displayed if validation fails, and validateAt="onServer" instructs ColdFusion to validate the page on the server after form submission. When ColdFusion processes this tag it generates a <input> tag (because Web browsers would have no idea what <cfinput> was anyway), along with other code that it writes for you, hidden form fields that contain validation rules that ColdFusion can process upon form submission.
<cfinput> must be used within <cfform> tags, you can't use <cfinput> with <form>. Doing so will throw an error.
Listing 13.4 contains an updated login screen, this time containing <cfform> and <cfinput> tags providing validation rules.
<!--- Name: login2.cfm Author: Ben Forta (ben@forta.com) Description: Form field validation demo Created: 12/21/04 ---> &l275> <head> <title>Orange Whip Studios - Intranet</title> </head> <body> <!--- Page header ---> <cfinclude template="header.cfm"> <!--- Login form ---> <cfform action="process2.cfm"> <table align="center" bgcolor="orange"> <tr> <td align="right"> ID: </td> <td> <cfinput type="text" name="LoginID" maxlength="5" required="yes" message="A valid numeric ID is required!" validate="integer" validateAt="onServer"> </td> </tr> <tr> <td align="right"> Password: </td> <td> <cfinput type="password" name="LoginPassword" maxlength="20" required="yes" message="Password is required!" validateAt="onServer"> </td> </tr> <tr> <td colspan="2" align="center"> <cfinput type="submit" name="submit" value="Login"> </td> </tr> </table> </cfform> </body> <l>
<form name="CFForm_1" action="process2.cfm" method="post" onsubmit="return _CF_checkCFForm_1(this)"> <table align="center" bgcolor="orange"> <tr> <td align="right"> ID: </td> <td> <input name="LoginID" id="LoginID" type="text" maxlength="5" /> </td> </tr> <tr> <td align="right"> Password: </td> <td> <input name="LoginPassword" id="LoginPassword" type="password" maxlength="20" /> </td> </tr> <tr> <td colspan="2" align="center"> <input name="submit" id="submit" type="submit" value="Login" /> </td> </tr> </table> <input type='hidden' name='LoginID_CFFORMINTEGER' value='A valid numeric ID is required!'> <input type='hidden' name='LoginID_CFFORMREQUIRED' value='A valid numeric ID is required!'> <input type='hidden' name='LoginPassword_CFFORMREQUIRED' value='Password is required!'> </form>
There is no <cfform> in this code, no <cfinput>, and no closing </cfform>. ColdFusion generated the standar259 form tags, and also made some other changes:
Listing 13.4 had no method specified, but <cfform> knew to automatically set method="post".
<cfform>, <cfinput>, and </cfform> were replaced with <form>, <input>, and </form> respectively.
Three hidden fields were added to the form, these contain the validation rules that ColdFusion will use when processing the form submission.
Other changes were made too, but those relate to client-side validation which we'll get to shortly.
Run login2.cfm and submit the form with missing or invalid values. You'll see an error screen like the one shown in Figure 13.4.
The screen shown in Figure 13.4 is the default validation error screen. This screen too can be changed using the <cferror> tag.
As you can see, the ColdFusion validation rules are simple and effective. And because the validation rules are embedded into the form itself, your forms and their rules are less likely to get out of sync.
The validation rules seen here were generated by ColdFusion automatically. If needed, you can embed the hidden validation rules yourself, although you'll seldom have to. In previous versions of ColdFusion this was necessary, as no automatic generation existed.
Of course, when validation errors occur the user will still have to go back to the form to make any corrections. The benefits of embedded validation rules are really only for developers. Embedded validation does nothing to improve the user experiencefor that you need client-side validation.