Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at Blue Smoke (New York City, NY) with:

Performing ColdFusion Processing After A CFLocation Tag

By Ben Nadel on
Tags: ColdFusion

The other day, I had a situation where a user had to hit a page where some processing would be triggered. The user was then immediately forwarded onto another page after the processing took place. The processing that took place did not take long, but it was not mission critical so I wanted to have the user forwarded before it was done completing (as they had no need to wait around). Normally, this is the kind of thing that I would perform using CFThread; but, since I was on a ColdFusion 7 box, I had to go another route.

I knew that CFLocation immediately aborts the current page request and forwards the user; but, I figured I would set up a base test just to make sure. I coded up a quick example, logging break points on the first page and the target page. Here is my first landing page:

  • <!--- Log pre-CFLocation value. --->
  • <cfset LogMessage( "Pre-CFLocation" ) />
  •  
  • <!--- Redirect user to next page. --->
  • <cflocation
  • url="page2.cfm"
  • addtoken="false"
  • />
  •  
  • <!--- Log post-CFLocation value. --->
  • <cfset LogMessage( "Post-CFLocation" ) />

As you can see, it logs a value, CFLocation's the user to the next page, then tries to log a second value. The next page is small and simply logs a test value:

  • <!--- Log landing page value. --->
  • <cfset LogMessage( "Target Page" ) />
  •  
  • <p>
  • Target Page
  • </p>

When I run the above landing page and get forwarded to my target page, the log file has the following:

Pre-CFLocation
Target Page

As you can see, the "Post-CFLocation" value never got logged. This is expected as the CFLocation aborted the current page request.

Now, to get around this, I have replaced the CFLocation tag with two CFHeader tags. The CFLocation tag is ultimately just setting the same header values; however, by doing this manually, we can get the same effect without having ColdFusion abort the current page request:

  • <!--- Log pre-CFLocation value. --->
  • <cfset LogMessage( "Pre-CFLocation" ) />
  •  
  • <!--- Tell the client that it will have to redirect. --->
  • <cfheader
  • statuscode="302"
  • statustext="Found"
  • />
  •  
  • <!--- Redirect user to next page. --->
  • <cfheader
  • name="location"
  • value="page2.cfm"
  • />
  •  
  • <!--- Flush header values to client. --->
  • <cfflush />
  •  
  • <!--- Log post-CFLocation value. --->
  • <cfset LogMessage( "Post-CFLocation" ) />

As you can see, we set the response status code and target location. The trick is then to call CFFlush directly afterwards. This immediately flushes the header values to the client (browser) which causes the redirect on the user's end. But, from ColdFusion's stand point, all we've done is committed the response so it keeps on processing the page. And, when we check out the log file, we can see that the post-CFLocation value is executed:

Pre-CFLocation
Post-CFLocation
Target Page

I don't think this is a solution that is all that critical; but, if you ever come to a scenario where you need an intermediary processing page, you can forward the user immediately with this technique without halting the processing of the current ColdFusion template.




Reader Comments

In many cases, though we can expect someone to jump on the thread and list the exceptions, you could throw something like this off to CFThread for post location logging. The bigger question in my case is what was the use case someone had that they did not do the logging before changing the location. (or rather trigger a thread to log stuff as I said above)

Reply to this Comment

@John,

I was working with tracking some landing page information. Basically, someone get's a link, hits the site, data gets logged, and then user is forwarded to actual landing page. A campaign tracker of sorts (very dumbed down).

Like I said, the logging that was going on was really fast, so it didn't really matter. But, I wondered if I could forward the user first and then take care of the tracking second (as it wasn't related to the following landing page).

I would have gone CFThread, but I was on a CF7 box.

Reply to this Comment

You may want to version check this. I seem to remember older versions of CF that sniffed for Location in the cfheader, and automagically munged it to a cflocation.

Reply to this Comment

Reminds me of the old CF_location tag that was kicking around especially in the FuseBox circles, that did the same thing, which helped solve an earlier bug of Cookies not being set on a cflocation'ed page.

One thing that I wonder about though, is in many frameworks the content is saved as a variable, and if this is the case, you'd get an error trying to flush. Any way around that? (And on the side Ben, what framework do you prefer?)

Reply to this Comment

@Rick,

Really? That seems really funky and uncalled for.

@Tim,

I was actually playing around with a custom tag earlier (trying to write one). The problem, however, is that inside of a custom tag, you can't call CFFlush as the tags are build to gather generated output in full before returning control to the buffer (or something - I can't quite find a way around it).

As far as framework, I have a methodology that I like to follow, but no formalized framework at the time.

Reply to this Comment

To quote an old blog entry of mine:

http://rickosborne.org/blog/index.php/2006/09/14/the-perils-of-search-engine-spider-sessions/

"Here's a hint: CF5. On CF5, any and all header information is lost on a redirect - this includes cookies, which are set in the HTTP headers. I tried cheating by setting my own status code and Location headers with cfcontent and cfheader instead of cflocation tags, but it doesn't work. It turns out that CF5 converts all attempts at redirects into the same cflocation-like result. Thus, no matter how you do it, CF5 recognizes what you are trying to do and "helps" you do it correctly. And, oh yeah, discards any cookie headers you might have tried to set."

Reply to this Comment

There's a related issue that CF does not process onRequestEnd() on a cflocation. If you track the functions in application.cfc it goes:

onRequestStart for page 1
cflocation to page 2
onRequestStart for page 2
onRequestEnd for page2

which is the screwiest page lifecycle I can think of. Adobe agrees - their own JEE-level page request filters all clean up properly - but the same courtesy is not extended to CF code.

For this reason we use the old cf_location trick, but for us flushing isn't an issue so it's not something we've solved.

Reply to this Comment

@Ben's: "I have a methodology that I like to follow, but no formalized framework at the [moment]."

High ass 5. It's unbelievably refreshing to see an unofficial ColdFusion authority like yourself publicly reiterate the harsh reality that for a LOT of projects, the pure, unadulterated RAD "framework" that is ColdFusion ( paired with a personal, conscious development approach ) can be just as, if not MORE effective than blindly and consistently consigning your development to a formalized, public community framework or platform.

Now I'm not badmouthing formalized or community frameworks. They can offer fantastic benefits and/or fantastic limitations and drawbacks ( all depending on the scenario ).

On top of that, very few frameworks are useful if the person utilizing them doesn't know what they're doing. At the same time, if someone is completely expert and natural with a particular framework, they can often use it to develop at a higher speed and with less maintenance potential than they could, say, without using it.

I think formalized frameworks are particularly beneficial to huge teams or projects that will be maintained by many different developers within the same company.

Props for keeping it real Ben.

Reply to this Comment

@Rick,

That's bananas! I think that must have been gone since CF6 as I have never come up against it.

@Jaime,

Yeah, it does seem a bit odd, especially considering that the "refresh" is actually something performed on the client (browser) and not on the server.

@David,

I have the benefit (or drawback) of not working on real teams. As such, there is no need for a common language framework. Not to say that I am all willy-nilly with my code - far from it. I use a front-controller (index.cfm) and my directory structure is quite standardized. I even have my own dependency injection framework ... better known as a "Factory Method". I simply have not felt the "pain" needed to make me sit down an learn a framework.

My real "pain point" right now is understanding OOP. OOP, of course, is completely separate from the concept of a framework and can be learned / implemented independently. Now, if only it wasn't so gosh darn hard for me to wrap my head around!

Reply to this Comment

i can speak only for railo, but railo has adapt how other vendors handle this situation.
railo definty abort execution of the page, the same way as a cfabort does.

java code of cflocation in railo (stripped)
rsp.setHeader("Connection", "close");
rsp.setStatus(statuscode);
rsp.setHeader("location", url);


try {
pageContext.forceWrite("... html message");
} catch (IOException e) {
throw new NativeException(e);
}
pageContext.getDebugger().setOutput(false);
throw new Abort(Abort.SCOPE_REQUEST);

Reply to this Comment

Here we go...

My dad is stronger than your dad.

Yeah, well my dad uses Railo.

So what, my dad uses ColdFusion.

Blah, you guys bust me up... my dad uses Blue Dragon!

(This should be an interesting ride. LOL)

Reply to this Comment

was never my idention to point in this direction, pherhaps i have made a failure in how i have written my text, sorry my english isn't so good to see the small nuance in the language.
wass just my 5c

greetings micha

Reply to this Comment

Michael: No man, don't worry about it. It was just a fun, harmless joke.

If I'd started saying things like, "Railo is an absolute bold-faced rip off of Adobe ColdFusion, that hired people right from the ColdFusion prerelease in probably the dirtiest competitive maneuver in the history of technology, and anyone that uses and supports it is directly contributing to the degradation and decline of the incredible future Adobe has for CF", then yeah, that would be something to address or freak out about. But who would say something like that? Not me.

Because I was, and I'm guessing John was too, just kidding around. Your English is fine.

Reply to this Comment

Hey, I have an interesting question, I think.

I have a flash movie posting form data to a hidden iframe on the page. The code in this hidden iframe calls a function on the parent page...

(script)
parent.reloadDesignLab('cid=#cookie.customerID#&sdid=#form.sdid#&xx=#qsD.xx#&saveas');
(/script)

...and I'd like the parent page to reload with the new info...

(script)
function reloadDesignLab(theLoad) {
cflocation('/DesignLab.cfm?' + theLoad + '&hoohah');
(/script)

... and although this reloads the page, it doesn't update the visible url.

Any thoughts?

Reply to this Comment

I have a project where I use the web servers 404 error handling to process a personalized url (PURL) so someone can goto http://www.something.com/theirname, parse the response query and look them up in a database to create a personalized experience. I had an issue with this using cflocation because is was unable to recognize actual pages and folders. I replace my cflocation with headers and a redirect and it's now working after 16 hours of trial and error (overnight). Thanks for the tip!

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.