| Previous | Next |
You may be wondering, "With all the advantages to
client variables, why not just use these exclusively?" That's a good
question—and, in fact, I recommend doing just that for all but the smallest of
applications. But before we rush into this, there are a few issues we need to
resolve.
One disadvantage to client variables is that because the
variables are stored on a physical medium, complex variables such as queries,
arrays, and structures present a problem. Variables in typical relational
databases must be simple datatypes such as strings, dates, and Boolean values.
Since the use of complex datatypes adds greatly to our code's simplicity and
clarity, we must find a way around this problem.
The other problem is the lifespan of client variables. While
session variables time out—and that timeout can be set to the exact number of
seconds—the timescale for client variables is much coarser. The only option you
have is in the ColdFusion administrator, where you are allowed to "purge data for clients that remain unvisited
for n days". You supply the value of n.
Luckily, there's a
way around this. Remember that client management also relies on the CFID/CFTOKEN pair to operate and the same method of expiring session variables by
resetting the CFID/CFTOKEN cookies we saw with sessions works with
client variables, too.
Brian Kotek, who has
written several ColdFusion tips for CNETBuilder.com, has created an excellent
custom tag, ClientTimout.cfm, to
handle the problem of timing out client variables. Here is the code:
<CFPARAM
NAME="CLIENT.CheckLastVisit"
DEFAULT="#CreateODBCDateTime(Now())#">
<CFSET Compare =
DateCompare(DateAdd("n", (ATTRIBUTES.TimeOut * -1),
CreateODBCDateTime(Now())), CLIENT.CheckLastVisit)>
<CFIF Compare IS NOT -1>
<CFSET
CALLER.TimedOut = "Yes">
<CFELSE>
<CFSET
CALLER.TimedOut = "No">
</CFIF>
<CFSET CLIENT.CheckLastVisit =
CreateODBCDateTime(Now())>
To
invoke the tag, you would make a call to this custom tag where it will be
called prior to every ColdFusion page—typically in the Application.cfm file..
<cf_ClientTimeout timeout="15">
The timeout property is the number of
minutes you wish for client variables to remain active after a new page
request. With this simple addition, you can have the granularity associated
with session variables without the drawbacks of sessions.
Because client variables are stored physically—in the
registry, in a cookie, or in a database—they can't hold complex variables. Try
executing this code and you'll get an error for your troubles:
<cfset client.str = StructNew()>
<cfset client.str.firstName = "Hal">
<cfset client.str.lastName = "Helms">
<cfset client.str.website =
"www.halhelms.com">
ColdFusion will tell you what you already know—client
variables must be simple variables. But complex variables, despite their name,
make code so much simpler to read and write that losing the ability to
use them would be an unacceptable trade-off for the benefits of client
variables.
The solution is to use the <cfwddx>
tag. This tag uses the Web Distributed Data eXchange specification to
"serialize" data into an XML string format. You can think of this as
a kind of transformative magic in which complex variables become simple ones:
<cfset str = StructNew()>
<cfset str.firstName = "Hal">
<cfset str.lastName = "Helms">
<cfset str.website = "www.halhelms.com">
<cfwddx
action="CFML2WDDX"
input="#str#"
output="client.me">
What's happening here?[1] The <cfwddx> tag
converts str, a structure, into a self-describing string, called a packet in
WDDX-speak. Here's what the packet looks like (I've added in some whitespace
formatting to make the structure of the packet clearer):
<wddxPacket version='1.0'>
<header></header>
<data>
<struct>
<var
name='FIRSTNAME'>
<string>Hal</string>
</var>
<var
name='LASTNAME'>
<string>Helms</string>
</var>
<var name='WEBSITE'>
<string>www.halhelms.com</string>
</var>
</struct>
</data>
</wddxpacket>
The complex variable has been "flattened" into
this string, where it can be stored in any of the client variables' datastores.
To use <cfwddx> to transform complex datatypes into simple
ones, first create the datatype as a local variable and do any manipulations
with it as a local variable, just as I've done with the local variable, str,
above.
To save the complex-datatype local variable as a client
variable, use the <cfwddx> tag, exactly as I've
shown it (substituting, of course, your own input and output values).
To reassemble the string into a complex variable, you need
to employ a little reverse magic, again using <cfwddx>
but this time using this as the action property:
action="WDDX2CFML"
Upon re-assembly, ColdFusion uses meta-information in the
string to put the complex variable back together.
<cfwddx
action="WDDX2CFML"
input="#client.me#"
output="str">
I think I'll go check out the free developer stuff at
#str.website#
The
action, CFML2WDDX,
converts ColdFusion variables into WDDX packets; the action WDDX2CFML
re-assembles the string packet into a complex ColdFusion variable.
WDDX was made to operate among different languages; there
exist WDDX implementations for JavaScript, Java, ASP, PERL—maybe others. You
can find out more information at www.wddx.org.
As you've seen, ColdFusion's persistent variables depend on
the presence of two cookies: CFID and CFTOKEN. That works fine in applications where you have
some degree of control over the browser environment and know that the user's
browser will accept cookies, but what if you are working in a more public
environment where you do not know whether users' browsers will have cookies
enabled?
In this case, ColdFusion allows for the CFID/CFTOKEN pair
to be passed explicitly from template to template as URL variables. A typical
URL link might look like this:
<a
href="aPage.cfm?cfid=#cfid#&cftoken=#cftoken#">Next</a>
If ColdFusion sees these URL parameters being passed, it
treats them just as if they were being passed as cookies. The only difference
from the developer's point of view is that while cookies are automatic, URL
parameters must be passed explicitly.
Fortunately, Macromedia has provided a nice shortcut—the
URLtoken variable—which lets the preceding code be replaced with this:
<a href="aPage.cfm?#URLtoken#">Next</a>
When submitting
forms, you can either make the CFID and CFTOKEN hidden form fields...
<form action="aPage.cfm"
method="post">
<input
type="Hidden" name="CFID" value="#CFID#">
<input
type="Hidden" name="CFTOKEN"
value="#CFTOKEN#">
...
...or you can use URLToken as part of the URL query string.
<form action="aPage.cfm?#URLToken#"
method="post">
...
And when using <cflocation>,
include the addtoken property:
<cflocation url="some_Url.cfm"
addtoken="Yes">
The biggest drawback to this scheme is the security
exposure: URL parameters can be seen and snooped by anyone sufficiently
interested. It's not that cookies are absolutely secure—but they are a little
harder to snoop. It's like putting a lock on a door. As my First Sergeant in
the U.S. Army once told me, "Locks only keep an honest person honest; they
won't stop a thief." I guess you could look at URL variables the same way.
[1]
While this is nice to know—akin to knowing how internal combustion engines in
automobiles work—it’s not really necessary, so feel free to enjoy the magic
without delving into the technology if you’d like.
| Previous | Next |