Thursday, June 27, 2002

New URLSessionFormat() Function
This first new feature is a real hidden gem that's not been played much at all. To understand its value, some background may be necessary. If you've needed to support session (or client) variables, then you know that these features rely primarily on the browser supporting cookies to be able to keep track of the user's identity (using what CF calls the CFID and CFTOKEN values it generates for each visitor).

Problems arise browsers that don't support (or are by mandate not allowed to accept) cookies visit your site. In that case, you must manually append the CFID and CFTOKEN to the URL used in any HTML you send to the browser that passes the user back to your server, such as <A HREF> and <FORM ACTION>. (The <CFLOCATION> tags also had an option called ADDTOKEN="yes" that accomplished the same thing).

If you've simply not been paying attention to this issue, then you may be suffering when users visit your site who don't support cookies. They never keep session variables from page to page, for instance. That can wreak havoc on a login process! And they also can't keep client variables from visit to visit.

But the converse is that you shouldn't ALWAYS append these values to a URL, because that leaves your site open to several potential problems. If someone passed a bookmark of a URL with a given CFID/CFTOKEN pair shown, the user receiving that bookmarked URL will also now use the first user's CFID/CFTOKEN pair. Have you ever heard of two people seeming to share a session? This is one of the ways it happens.

Another challenge is that someone can just guess at CFID/CFTOKEN values when displayed this way on a URL (more on another way to solve that problem in a moment).

So the optimal way to handle this (pre-CFMX) is to somehow test if cookies are supported for the user running the template, and then only if they're not, append the CFID/CFTOKEN value. The code to do that isn't too difficult, but getting it right and then placing that CFIF logic around every instance of <A HREF>, <FORM ACTION>, or <CFLOCATION> (deciding whether to use ADDTOKEN="yes") could be challenging.

Enter the new URLSessionFormat function! With this simple function, you can now let CFMX worry about whether to append the CFID/CFTOKEN pair (and/or the JsessionID if using J2EE sessions, as discussed in a moment.) Here's a simple example:
<cfoutput>
<a href="#URLSessionFormat("MyPage.cfm?name=bob")#">some link</a>
</cfoutput>

If CFMX detects that the browser executing this template isn't passing the needed cookie (CFID/CFTOKEN for CF session and client variables, JsessionID for J2EE session variables), then it will turn that output HTML into the following:
<a href="MyPage.cfm?name=bob&cfid=xxxx&cftoken=xxxxxxxx">some link</a>

On the other hand, if CFMX detects that the needed cookies are being passed in by the browser, it leaves the identifiers off. Very cool! And note that it's smart enough to realize if the URL already has something in the query string (as it does above), in which case it uses an ampersand (&) to append the identifiers. Ta da! (In that example, I'm not yet showing what it looks like if J2EE sessions are being used.)

Now, there is one gotcha. If you also need to use a URLEncodedFormat function for some part of the query string that has embedded spaces or special characters, it could become pretty cumbersome to use both functions in a single line of code, with embedded functions and strings within those functions. The following looks butt-ugly, but will indeed work:
<cfoutput>
<a href="#URLSessionFormat("MyPage.cfm?name=#URLEncodedFormat("billy bob")#")#">some link</a>
</cfoutput>

One other observation: I've noticed that when using J2EE sessions, it's possible for the resulting URL to appear in the format: MyPage.cfm;JSESSIONID=8030949691025145133137?name=bob. Note, however, that in this case the JSEssionID is being appended to the filename separated by a semicolon, rather than appended to the query string. Still, when it's happened, the URL has functioned as expected.

It may be worth pointing out that technically, this function's name isn't completely accurate. It's needed not just for session variable handling but client variable handling as well. In other words, if you use client variables but not session variables, this is still the way to guarantee that the CFID/CFTOKEN pair needed for client variable support as well is sent to browsers that don't allow cookies. (Indeed, if Client variables are in use in that previous example showing the JSessionID, then the CFID and CFTOKEN would be appended to the query string as they were above.)

You also shouldn't dismiss browsers that don't support cookies as being antiques not worthy of your concern: there are many organizations that force users to disable cookie support in their browsers. This solution helps you support them as well.

No comments: