ColdFusion Scope Existence During Various Request Types And Events

I tend to hear a lot of uncertainty about the existence of various ColdFusion scopes during different types of page requests and application-level events. In particular, there seems to be a lot of confusion as to what ColdFusion scopes exist during remote web service calls (external calls made to local ColdFusion components with "remote" access). Just the other day, I heard someone say that the "APPLICATION" scope doesn't exist during a web service call. Clearly this is inaccurate as it would paralyze the entire web service.

To get us all on the same page, I thought I would throw this test together to clearly demonstrate which ColdFusion scopes exist during various request methods. The key to this demonstration is to accurately log the scope existence at the application-event level. To do this, I created a simple Application.cfc that tracks the primary global scopes in the pseudo constructor as well as the three "On Start" events:

<cfcomponent

output="false"

hint="I provide application settings and event wiring.">

<!--- Define application settings. --->

<cfset THIS.Name = "ScopeTesting" />

<cfset THIS.ApplicationTimeout= CreateTimeSpan( 0, 0, 0, 30 ) />

<cfset THIS.SessionManagement = true />

<cfset THIS.SessionTimeout = CreateTimeSpan( 0, 0, 0, 30 ) />

<cfset THIS.SetClientCookies = true />

<!--- Define the log file path. --->

<cfset THIS.LogFilePath = (

GetDirectoryFromPath( GetCurrentTemplatePath() ) &

"log.txt"

) />

<!--- Log pseudo constructor. --->

<cfset THIS.LogScopes( "PseudoConstruct" ) />

<cffunction name="OnApplicationStart">

<cfset THIS.LogScopes( "OnApplicationStart" ) />

<cfreturn true />

</cffunction>

<cffunction name="OnSessionStart">

<cfset THIS.LogScopes( "OnSessionStart" ) />

</cffunction>

<cffunction name="OnRequestStart">

<cfset THIS.LogScopes( "OnRequestStart" ) />

<cfreturn true />

</cffunction>

<cffunction

name="LogScopes"

access="public"

returntype="void"

output="false"

hint="I check to see if the given global scope exists.">

<!--- Define arguments. --->

<cfargument

name="Event"

type="string"

required="true"

hint="I am the name event in which we are testing."

/>

<!--- Define the local scope. --->

<cfset var LOCAL = {} />

<!--- Append selected scope name. --->

<cffile

action="append"

file="#THIS.LogFilePath#"

output="+--- #ARGUMENTS.Event# -----------------+"

addnewline="true"

/>

<!--- Loop over the scopes. --->

<cfloop

index="LOCAL.ScopeName"

list="APPLICATION, SESSION, CGI, REQUEST, FORM, URL, COOKIE"

delimiters=", ">

<!--- Check to see if scope exists. --->

<cfif IsDefined( LOCAL.ScopeName )>

<!--- Add a special suffix for testing. --->

<cfif (LOCAL.ScopeName EQ "SESSION" )>

<cfset LOCAL.Suffix = " :: #SESSION.CFID# :: #SESSION.CFTOKEN#" />

<cfelse>

<cfset LOCAL.Suffix = "" />

</cfif>

<!--- Log scope existence. --->

<cffile

action="append"

file="#THIS.LogFilePath#"

output="#LOCAL.ScopeName# :: Yes#LOCAL.Suffix#"

addnewline="true"

/>

<cfelse>

<!--- Log scope inexistence. --->

<cffile

action="append"

file="#THIS.LogFilePath#"

output="#LOCAL.ScopeName# :: No !!!!!!"

addnewline="true"

/>

</cfif>

</cfloop>

<!--- Add new line break. --->

<cffile

action="append"

file="#THIS.LogFilePath#"

output=""

addnewline="true"

/>

<!--- Return out. --->

<cfreturn />

</cffunction>

</cfcomponent>

Notice that all we are doing in the various application level events is logging the existence of the following scopes:

APPLICATION

SESSION

CGI

REQUEST

FORM

URL

COOKIE

In addition to logging the existence, I am treating the SESSION scope as a special case; if we are checking the SESSION scope then I am also outputting the CFID and CFTOKEN values. This way, when I hit the page several times in a row, we can see if the session is maintained across requests.

The Application and Session timeouts are purposely small so that each group of successive requests will be executed in a "new" application environment. Each of these test will utilize 2 successive page requests.

ColdFusion Template Request

In this first test, we are simply going to request a ColdFusion template as we would with any application request. Here is what the log file reveals:

There's nothing special going on here. The APPLICATION and SESSION scopes never exist in the pseudo constructor, even on the second page request. Once the application has booted up (after the first request), the subsequent request only fires the pseudo constructor and the OnRequestStart() application event as expected. Notice also that the session information (CFID and CFTOKEN) are maintained across requests.

Remote Testing

For the following tests, I created an insanely simple remote-access ColdFusion component:

<cfcomponent

output="false"

hint="I provide remote methods.">

<cffunction

name="Test"

access="remote"

returntype="string"

returnformat="json"

output="false"

hint="I return a value.">

<cfreturn "Method Executed" />

</cffunction>

</cfcomponent>

As you can see, all it does is return a static string in JSON format.

ColdFusion Component URL-Based Request

In this test, we are going to access the remote method of the above ColdFusion component using a standard, browser-based URL:

Here, we can see that the URL-based remote component requests act exactly the same as a URL-based ColdFusion template requests. Even the session information is maintained across both requests. This makes sense since both requests are being made by the client browser and therefore will properly pass along the user cookies with each request.

ColdFusion Component WSDL-Based Request

Now, things get a little more exciting; rather than accessing the ColdFusion component using the URL, we are going to create a ColdFusion template (in a separate application environment) to make a WSDL-based web-service request to the remote component:

Here's where we actually see some interesting activity: the FORM scope does not exist in a WSDL-based web service request. Additionally, the session IDs are not maintained across the request, but this is to be expected as they are being made by the ColdFusion server, not a traditional browser/client. Despite the lack of session stickiness, the APPLICATION scope does exist and acts as expected across the two web service requests.

I think that just about covers the majority of requests we are going to be making to a ColdFusion server. I hope this demonstration has helped to clear up any confusion about which ColdFusion scopes exist during the various request types. Really, the only thing of any shock-value here is the fact that the FORM scope doesn't exist during a WSDL-based request. Other than that, all requests to the ColdFusion server have very much the same behavior.

Reader Comments

It's a bit OT, but what's the deal with using this.X() to call methods in your CFC? You should just be using X() to call methods defined in the same CFC. When you do this.X(), it acts as an 'outside' call, so if X had access=private, you would actually be blocked.

I just love scoping stuff :) It's completely an emotional issue. It gets me into trouble sometimes because there is a bug in ColdFusion that does not allow me to use named-arguments in privately scoped method calls:

<cfset VARIABLES.Method( Name=Value ) />

... throws a bug, so I have to compromise sometimes if I need named arguments on private methods.

Scoping just makes me feel comfortable; and it makes a strong statement about the implementation / intent of my methods (some might say this is exactly the reason *not* to do it - so, like I said, its emotional).

I like scoping too as well - but this just seems like a mistake. It's like using your phone to call your wife when she is sitting next to you in the room. If you want to use name/val pairs for a local method and you can't do variables.x(....), then you could always use cfinvoke method="X". Its a lot more typing though.

This REALLY bugs me - talk about emotional though - and OT to this entry, which is pretty darn interesting.

It gets a little more complicated, its a request made from either Flex/Flash to domain.com/flex2gateway/Or in my case, flash media server to /flashservices/gateway/And with your test files, it was easy to test my situation: (Sorry for the long post)+--- PseudoConstruct -----------------+APPLICATION :: No !!!!!!SESSION :: No !!!!!!CGI :: YesREQUEST :: YesFORM :: YesURL :: YesCOOKIE :: Yes

Useful stuff as always, Ben. Since this may serve as a bit of a compendium for some, I would think a couple other rather common scenarios worth pointing out would be 1) a CFHTTP invocation of a CFC (or the equivalent in other languages calling into a CF page), and 2) an Ajax call to a CFC.

In case 1), I'm referring to how one might call a CFC like your first attempt (/Test.cfc?Method=Test) via a URL from a programmatic client like CFHTTP (versus a programmatic client calling via soap). In terms of variables present, I suspect it would act like your invocation of it as a soap web service, in that it too would not have the client/session id cookies passed in, unless one manually added them using CFHTTPPARAM (or the equivalent if calling from another language).

In case 2), I suspect it may act more like your first call (from a browser), since Ajax requests also come from a browser and therefore should present the cookies. I'll let others with more experience with different Ajax frameworks speak up on any variations they may have noticed.

I've not noticed how either respond with respect to FORM variables, so if you or anyone else runs a test, it would be good to drop a comment here. As it is, I was just checking in on some things quickly while on vacation. Happy holidays, all.

For Case 1, I am pretty sure a CFHTTP call would act like a standard browser invocation. I think that's how we get screen-scraping to work. However, as you are saying, the session will probably restart with every CFHTTP request unless the cookies are manually passed-back.

For Case 2, I have field-experience that that works; I have some AJAX apps that are session-based and make AJAX calls that require sessions to work and I am not passing anything session-based back.

As for how CFHTTP and SOAP requests differ, I don't really understand that stuff. I do know that if you access the WSDL file directly in the browser:

URL: Test.cfc?wsdl

... the FORM scope does not exist. So, whatever mechanism handles the SOAP request seems to also handle the WSDL request as well (or maybe those are actually the same request - one with a SOAP body, one without).

What I think would be interesting is how the CGI.http_user_agent comes across in the various calls. I wonder if SOAP uses the browser agent, or if it goes through a ColdFusion-proxy which announces itself horizontally as the "ColdFusion bot".

Speaking of ColdFusion proxies, I attended a Simon Free presentation last week - he was talking about AMF web services; I wonder if AMF comes across as a ColdFusion-proxy user agent or if the browser user-agent gets passed-through.

To clarify: AJAX communication to a CFC (let's say with jQuery), would only have access to the application and session scopes if the CFC resides in the same application folder right?

Related question: Assuming the CFC is within the same application, is it a horrible idea to directly access the user's session scope from the CFC methods when handling an AJAX request?

Normally, to follow standard OO best practices, a CFC's methods should only handle arguments passed to it… but in the case of an AJAX request, the Javascript client request doesn't know about the user's Coldfusion session scope natively. may not be safe to have Coldfusion pass those vars to the client, just so they can be sent back to the CFC.

I would love to hear your thoughts on this topic if you get the opportunity.

"AJAX communication to a CFC (let's say with jQuery), would only have access to the application and session scopes if the CFC resides in the same application folder right?"

Please remember - the web server has no concept of AJAX. AJAX is just HTTP via JavaScript. Therefore, the 'rules' here are no different.

"Assuming the CFC is within the same application, is it a horrible idea to directly access the user's session scope from the CFC methods when handling an AJAX request?"

I've blogged on this before - in my opinion, there is nothing wrong with a "Service" CFC directly accessing these scopes. As we all know, CFC creation can be slow, and it can be complex. If you have gone through the trouble of using something like ColdSpring to set up the CFCs, it is silly not to provide access to them via the scopes and your service CFC. (In fact, ColdSpring supports generating these service objects for you.)

I am having trouble making a variable, which is part of a returned structure, available to another cffunction in the same component (in CF7).

If I refer to a cfset that creates a randomID, the call gets made twice. Once in the first cffunction and again in the second so the randomIDs will never match.But then I can't figure out how to get the randomID out of the structure being returned by the first cffunction to compare it against the randomID being returned by the argument. RandomID is in the structure which is <cfreturn returnStruct />

I have tried setting the scope to THIS, VAR and VARIABLES to no avail. Any help or pointers somewhere would be appreciated.

The first <cfobject> tag instantiates the component putting it into memory and then it is called twice using its name by the <cfinvoke> tags.

Ok that's all straightforward but when you call it as a webservice its much more difficult since you have to put it into memory on the remote machine and then call it twice remotely which you can't do directly.

To put it into memory you would normally write someting in the Application.cfc like:

No problem at all. Good luck. I think a database should work just as well; there's a bit of a performance overhead to hitting a database vs. getting something cached in the application scope. But, using a database is definitely more scalable.

At least, that is what happens for me... if it isn't for you, please let me know. The ability to reference application.applicationName from within a CFC would solve a problem I am trying to figure out now.

Ah, I've been burned by the rogue Application.cfm file before! So frustrating. One time, I actually wrote a whole blog post about an issue I was having, cause I thought it was a bug; then, right before I posted it, the thought popped into my mind that I might have an App.cfc issue. Turned out, I didn't have one in my testing directory... but I had one like 4 directories up, that was messing with my request processing.

For the life of me, using CF8 at work, the Request Scope simply IS NOT available when I am calling a CFC via AJAX.

I've had to jerry-rig by having the Calling page send a variable which is the Request-scope variable I want to maintain all the way through the AJAX call and back. I really hate that I've yet to comprehend why the scope variables disappear on me.