This week I exchanged a few emails with Damien Bruyndonckx concerning an interesting bug he found with flash remoting and onCFCRequest, a new feature added to ColdFusion 9. (You can see a quick review of this here.) I was able to confirm the issue he found (which he has already logged a bug for) and I thought I'd share with you guys the details. All credit for this goes to Damien - I'm just the messenger.
Damien was making use of a simple Flex project that used Flash Remoting to hit a CFC on his ColdFusion server. As part of his process, he made use of onCFCRequest. If you follow the docs, you will see that the "skeleton" of this method should be like so:
public void function onCFCRequest(string cfcname, string method, struct args) {
}
As you can see - there are three arguments. The name of the CFC. The method being run. And finally - a structure of arguments to pass along to the method. However, as soon as he added this method to his Application.cfc file, he got an error concerning the type of the args argument. Turns out it was being passed as an array, not a struct. Upon further testing, he discovered that whenever he called his services like so:
someRemoteService.someMethod(x,y,z)
The type of args was always an array. (He also saw the same if the method had no arguments at all.) But if he switched to using a basic object:
data = new Object();
data.x = "a";
data.y = "b";
data.z = "c";
someRemoteService.someMethod(data)
Then the args argument was a structure again. Whats interesting though is that when he repeated his tests over HTTP (both with simple HTTP and cfajax), the args argument was always a structure. This appears to only be an issue when using Flash Remoting.
Of course the fix is easy enough - switch the method signature to be "any" and it will always work. What's interesting is that if you then pass args along to your CFC using argumentCollection, whether it be an array or struct, it continues to work ok.
Has anyone else run into this?
Archived Comments
I just built a flex project and ran into same issue.
All my target functions had to be returntype='any' . It would be nice to see onCFCRequest pick up the targeted cfc methods returntype.
Here is what I ended up doing. I created a structure that allowed me send info about the call in the returned structure. This also allows you to capture a session timeout in your flex app if you want too code for that.
This is for only authenticated users and all requests come thru flex.
I have not used this in a production yet.
<code>
<cffunction name="onCFCRequest" access="remote" output="true" returntype="struct" >
<!--- Define arguments. --->
<cfargument name="cfcname" type="string" required="true" hint="component requested by the user." />
<cfargument name="method" type="string" required="true" hint="method requested by the user." />
<cfargument name="args" required="true" hint="collection sent by the user." />
<cfset var mainresult = StructNew()>
<cfset mainresult.app = "appname">
<cfset mainresult.cfcname = "#arguments.cfcname#">
<cfset mainresult.method = "#arguments.method#">
<cfset mainresult.incomingvalues = "#arguments.args#">
<!--- Check Session --->
<!--- Get session and look for --->
<cfset var pc = getpagecontext()>
<cfset var s = pc.getSession()>
<cfif isdefined("s")>
<cfset var REQ = pc.getRequest()>
<cfif REQ.isRequestedSessionIdFromURL() >
<!--- You can implement to deny access or to track this type of access at this point. --->
<cfset mainresult.status = "Invalid session - session from url">
<cfset mainresult.cfresult = "invalidsession">
<cfreturn mainresult>
<cfelse>
<!--- HTTP Session found using Cookie value --->
<!--- Check to see if user is calling authenticate method --->
<cfif arguments.method EQ "authenticate">
<cfinvoke component="#arguments.cfcname#" method="#arguments.method#" returnVariable="result" argumentCollection="#arguments.args#" />
<!--- Only let calls go thru for authenticated sessions. --->
<cfif not isdefined("session.user.username")>
<cfset mainresult.status = "Invalid session - session not defined">
<cfset mainresult.cfresult = "invalidsession">
<cfreturn mainresult />
</cfif>
<cfif len(session.user.username) LT 1>
<cfset mainresult.status = "Invalid session - no user assigned to session">
<cfset mainresult.cfresult = "invalidsession">
<cfreturn mainresult />
</cfif>
<!--- User authenticated --->
<cfset mainresult.cfresult = true>
<cfreturn mainresult>
</cfif>
</cfif>
<cfelse>
<!--- No http session was found --->
<cfset mainresult.status = "Invalid session - no session found">
<cfset mainresult.cfresult = "invalidsession">
<cfreturn mainresult>
</cfif>
<!--- One more check to make sure we have a valid session. --->
<cfif not isdefined("session.user.username")>
<cfset mainresult.status = "Invalid session - session not defined">
<cfset mainresult.cfresult = "invalidsession">
<cfreturn mainresult />
</cfif>
<cfif len(session.user.username) LT 1>
<cfset mainresult.status = "Invalid session - no user assigned to session">
<cfset mainresult.cfresult = "invalidsession">
<cfreturn mainresult />
</cfif>
<cfinvoke component="#arguments.cfcname#" method="#arguments.method#" returnVariable="result" argumentCollection="#arguments.args#" />
<cfset mainresult.cfresult = result>
<cfreturn mainresult />
</cffunction></code>
Just found it. Thanks for the help.