Previous Next
ColdFusion Foundations

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