The previous lesson covered how a form's fields arrive in the form scope, and introduced cfform's cfinput control. This lesson covers validating what actually arrives: cfinput's client-side checks, and, more importantly, isValid() and cfparam for validating it again on the server, since client-side validation only stops a well-behaved browser.
Learning Objectives
After completing this lesson, you'll be able to:
- Use cfinput's validation attributes for client-side checks.
- Validate a submitted value's real type or format server-side with isValid().
- Use cfparam to enforce a field's type and presence.
- Explain why server-side validation is required regardless of what the browser already checked.
Why Both Layers Exist
User Submits
cfinput / HTML5 Checks (Browser)
Reaches the Server Regardless
isValid() / cfparam Confirms It's Actually Safe
A request sent directly to the action page, bypassing the browser entirely, skips client-side validation completely. The server-side check is the only one that can't be skipped.
Client-Side: cfinput's Validation Attributes
| Attribute | Meaning |
|---|---|
| required | Marks the field mandatory (default false) |
| validate | A built-in type: date, eurodate, time, float, integer, telephone, zipcode, creditcard, social_security_number, email, or regular_expression |
| pattern | A custom regular expression, used with validate="regular_expression" |
| range | Minimum,maximum bounds for a numeric field |
| message | The error text shown on validation failure (one message per field) |
| validateAt | When the check runs: onSubmit (default), onBlur, or onServer |
Adobe's own documentation flags a real gotcha: the validation algorithm used for date/time values in onSubmit/onBlur checks is different from the one used server-side, the two can genuinely disagree on the same input.
A Real Example: Client-Side Validation on a Registration Form
<cfform action="process-registration.cfm" method="post">
<cfinput type="text" name="phone" validate="telephone" message="Enter a valid phone number.">
<cfinput type="text" name="email" required="yes" validate="email" message="Enter a valid email address.">
<cfinput type="text" name="age" validate="range" range="18,120" message="Age must be between 18 and 120.">
<cfinput type="submit" name="submitBtn" value="Register">
</cfform>Server-Side: isValid()
isValid(type, value) checks whether a value actually matches a given type or format, returning a plain boolean rather than stopping execution, which makes it the right tool for building a real list of field-level errors.
| type | Checks |
|---|---|
| A valid email address format | |
| integer / float / numeric | The value is a number of that kind |
| range | A numeric value falls within a min/max, passed as the third and fourth arguments |
| regex / regular_expression | The value matches a supplied pattern |
| telephone / zipcode / creditcard / ssn | U.S.-specific formats: phone number, ZIP code, a 13–16 digit credit card number (mod10), or a social security number |
| url | A valid http, https, ftp, file, mailto, or news URL |
| guid / uuid | A properly formatted GUID or UUID |
| variableName | A string that's a valid CFML variable name |
<cfif structKeyExists(form, "submitted")>
<cfif not isValid("email", form.email)>
<cfset fieldErrors.email = "Please enter a valid email address.">
</cfif>
<cfif not isValid("range", form.age, 18, 120)>
<cfset fieldErrors.age = "Age must be between 18 and 120.">
</cfif>
</cfif>Server-Side: cfparam for Enforced Type and Presence
<cfparam name="form.age" type="numeric">
Unlike isValid(), cfparam doesn't return a boolean, it throws an exception immediately if form.age doesn't exist or isn't numeric. That makes it a good fit for a field the request genuinely can't proceed without, ideally paired with a cftry/cfcatch from Module 12 for a graceful message instead of the default error page.
A Real Example: Building a Structured fieldErrors Response
This combines this lesson with the throwing-exceptions lesson's structured extendedInfo pattern, checking each field with isValid(), collecting every failure into one struct instead of stopping at the first one.
fieldErrors = {};
if (!structKeyExists(form, "newsletter")) {
form.newsletter = "false"; // unchecked checkbox never submits
}
if (!isValid("email", form.email)) {
fieldErrors.email = "Please enter a valid email address.";
}
if (!isValid("range", form.age, 18, 120)) {
fieldErrors.age = "Age must be between 18 and 120.";
}
if (structCount(fieldErrors) > 0) {
throw(type = "Validation", message = "Registration validation failed.", extendedInfo = serializeJSON(fieldErrors));
}Common Beginner Mistakes
Relying on cfinput/HTML5 client-side validation alone
A request sent directly to the action page, bypassing the browser, skips it entirely. Server-side validation with isValid() or cfparam is the only check that actually can't be bypassed.
Assuming cfparam's type check gracefully returns false on failure
It throws an exception, it doesn't return a boolean the way isValid() does. Wrap it in cftry/cfcatch if a graceful message is needed instead of the default error page.
Assuming client-side and server-side date/time validation always agree
Adobe's own documentation notes the algorithms differ between onSubmit/onBlur validation and server-side validation, the same date/time input can pass one and fail the other.
Stopping at the first invalid field instead of collecting every error
Checking each field with isValid() and collecting every failure into a struct gives the user one useful response instead of forcing a resubmit-and-discover cycle for each field.
Best Practices
- Validate server-side every time, regardless of what cfinput or HTML5 already checked in the browser.
- Use isValid() for checks that should collect into a field-level error list rather than hard-stop the request.
- Reserve cfparam for fields the request genuinely cannot proceed without, and pair it with cftry/cfcatch for a graceful failure message.
- Default a possibly-missing checkbox/radio field (from the previous lesson's gotcha) before validating it.
Interview Questions
Why is server-side validation still required if cfinput already validates on the client?
A request sent directly to the action page, bypassing the browser entirely, skips client-side validation completely. Only a server-side check can't be bypassed.
What's the difference between isValid() and cfparam for validating a field?
isValid() returns a boolean, letting the code decide what to do and collect multiple errors. cfparam throws an exception immediately if the check fails, better suited to a field the request can't proceed without at all.
Name three type values isValid() accepts.
email, integer (or float/numeric), and range are common ones; regex, url, creditcard, zipcode, and telephone are also supported.
What real gotcha exists with date/time validation specifically?
Adobe's own documentation states the validation algorithm used for date/time values in client-side (onSubmit/onBlur) validation differs from the one used server-side, the same input can pass one and fail the other.
Summary
In this lesson, you used cfinput's validation attributes for client-side checks, validated real submitted values server-side with isValid() and cfparam, built a structured field-level error response, and covered why server-side validation is required no matter what the browser already checked.
What's Next?
The next lesson covers URL parameters: reading the url scope, and safely defaulting or validating a value that arrived in the query string instead of a submitted form.