Deep dive into CFCs and Requests

This post is more than 2 years old.

Last week I had an interesting discussion with Jaana G., a ColdFusion developer, who wasn't very clear on how CFCs operate over the wire. She had used CFCs as web services but didn't quite get how to use them in other ways - like for Ajax applications. I did a quick check of the docs and found this page: Invoking component methods by using a URL. While this explains some of the feature, it completely skips over the returnFormat portion and therefore wouldn't be too useful for Ajax developers. I thought then it might be a good idea to wrote up a guide that covers exactly what happens when you work with CFCs via HTTP requests. Any comments or corrections would be greatly appreciated.

Let's begin slowly with a simple CFC. The code below will be used for our demonstration.

component {

public string function helloWorld(string name="Nameless") {
	return "Hello, #arguments.name#";	
}

}

Nothing too complex here, right? Just one simple helloWorld method with an optional name argument. Given this, when you open your browser and point to the URL, in my case, http://localhost/test.cfc, you get...

Unless you have both your CF Admin and RDS passwords turned off, you are prompted to enter one of them. If you do, you then get a description of the CFC and it's methods:

This is a pretty handy service actually. The more you describe your CFC, for example, adding hints to your method and arguments, the more documentation you will get. However, keep in mind that it is only 'safe' on a development server. You won't have the admin unprotected anywhere else (I hope!) so this is really just for developers. (If you want a copy of those nice docs without sharing your CF Admin password, consider the cfcToPrinter udf.) So to recap - if you request a CFC just by file name with nothing else then you get either the login prompt or the self-generated documentation.

So how do we begin actually using our CFC over the wire? First off - ColdFusion is going to prevent you from remotely executing any method that is not marked "remote". In the code block above we had used "public". So first off, let's change that real quick:

component {

remote string function helloWorld(string name="Nameless") {
	return "Hello, #arguments.name#";	
}

}

There are thee main ways you will see people use CFCs remotely:

  1. Web Services
  2. Flash Remoting
  3. Straight HTTP Calls

In this guide I'm focusing on the last item above, but let's quickly cover the first two as well. In order for a CFC to be used as a web service, the client calling your CFC simply changes the URL to include ?wsdl as the end. So my previous url of localhost/test.cfc becomes localhost/test.cfc?wsdl. That's it. If you open the CFC with your own browser and enter that URL you will see the response is totally changed:

What you are seeing there is the "WSDL" for the web services. You can think of this as documentation for web service clients. You can read this - much like you can build your own airplane to travel, but frankly, you really don't want to. If you want your CFC to be used as a web service by someone else, you simply tell them the URL. Using the web service is a matter of what their client is. As we know, in ColdFusion it is pretty simple. Once you createObject the web service, you can treat it like a simple library.

I'm not going to cover Flash Remoting at all - outside to say that it just plain works. If you are using Flex than you will have a RemoteObject tag and will address the CFC via "dot" notation. So given that my CFC was called test and was in web root, I'd just use "test". If my CFC had been in a remote folder under a subdirectory called tests, I'd use "remote.tests.test." Nothing else is required. There's things you can do to optimize your code more for the Flex/Flash client, but again, I'm not going to spend much time on that now. (And by the way, it works just as easy for Flex/Flash on the mobile devices as well.)

So now we are on our third option - straight HTTP calls. The documentation I had linked to earlier discusses both URL and Form based requests. It basically boils down to this - you must tell ColdFusion what method you want to run. In URL form, that's just by adding a method attribute. In form form (oops, form mode let's say), it's just adding a method form field. So for example, I'd change my URL to this to invoke my helloWorld method: localhost/test.cfc?method=helloWorld. In a form, I'd include a field, typically hidden, that used method for a name and helloWorld for a value. Basically, the method name/value pair is special in regards to running CFCs. So what happens when I try that with my CFC?

Nice and simple, right? If I want to include a name, then I'd just add another argument: localhost/helloWorld.cfc?method=helloWorld&name=Darth

That's it, right? Nope. View source and you will next see the other crucial change - the encoding:

<wddxPacket version='1.0'><header/><data><string>Hello, Darth</string></data></wddxPacket>

Woah, what the heck is that? Well what you are seeing is ColdFusion encoding the result. For a string it seems a bit pointless, but imagine for a minute you were returning an array of values. HTTP responses have to text or binary. Binary wouldn't work (most of the time). How would you represent then - in text - an array? XML encoding allows for that. This string version of the data an more easily be parsed remotely and converted back into real data. But WDDX? Most likely this is not what you want. Luckily ColdFusion 9 (and I'll talk a bit about earlier versions below) provides more control via the returnFormat attribute. There are three ways data can be returned over the wire:

  1. WDDX - This is the default and is a XML version of your data
  2. Plain - This is a "Don't touch anything" version. It only works for simple values, like strings, dates, numbers.
  3. JSON - This is the most preferred format. JSON is slimmer than XML and understood by more clients, including libraries like jQuery.

To control this behavior, you have two choices (technically 3 but we're going to ignore one for now). You either specify returnFormat in your CFC function as an argument or you pass it over the URL. For the most part, I recommend specifying it in the URL, like so: localhost/test.cfc?method=helloWorld&returnFormat=plain&name=Darth. If you run that using my test code, you will see a plain text result. (But be sure to View Source!) Let's add another method though so we can see an example of something a bit more complex:

component {

remote string function helloWorld(string name="Nameless") {
	return "Hello, #arguments.name#";	
}

remote array function getNames() {
	return ["Ray","Scott","Todd","Jason","Booger"];
}

}

I've added a new function to my CFC called getNames. It returns an array of 5 names. Watch what happens when we return it using the 3 different return types:

localhost/test.cfc?method=getNames&returnformat=wddx
<wddxPacket version='1.0'><header/><data><array length='5'><string>Ray</string><string>Scott</string> <string>Todd</string><string>Jason</string> <string>Booger</string></array></data></wddxPacket>

Note - I added a space or two to make it wrap.

localhost/test.cfc?method=getNames&returnFormat=json
["Ray","Scott","Todd","Jason","Booger"]

localhost/test.cfc?method=getNames&returnFormat=plain

As you can see, trying to return a complex value "plainly" is not possible.

So I hope this helps. I want to leave you guys with a few other things to consider as well. Consider this extra reading material and if your mind is swimming already, stop for now.

  • So what about versions of ColdFusion earlier then 9 and return types? In ColdFusion MX, the result was always WDDX. Period. If you wanted anything like, like JSON for your jQuery, you pointed to a CFM instead. The CFM would run the CFC and create a JSON response. ColdFusion 7 added a modification where if returnType (not format, type) was XML, then it would NOT WDDX-ize the result. This was useful for returning XML but not JSON obviously. In ColdFusion 8 then we finally got returnFormat to make it more explicit.
  • There is another special argument - queryFormat - that modifies how queries are encoded. It's discussed here and in earlier blog posts.
  • Did you know that ColdFusion 901 made it possible to use CFCs outside of web root? This feature is limited to binding and AjaxProxy - you can't use it with your jQuery code - but it is a new feature. Check the 901 release notes for more information. (Here is the link I have for the release notes - it isn't loading for me right now so good luck: http://www.adobe.com/support/documentation/en/coldfusion/releasenotes.html)
  • What about Application.cfc? A request to load a CFC is like any other request. If this is the very first hit, onApplicationStart is run. Ditto for onSessionStart. In earlier versions of ColdFusion onRequest would fire and could possibly break your request. ColdFusion now has an onCFCRequest method instead that can handle these requests. This isn't a feature most people use though.
  • A common question I get is - what about CFCs I set up in onApplicationStart? CFCs that are initialized with variables like for my DSN? There's a few ways to handle them, but the simplest is to create another CFC that just runs as a proxy. So if you have a CFC called blog, for example, defined with a DSN and other variables, and it's stored in the Application scope, you can make a new CFC that works with that CFC. Maybe doing something as simple as: return application.blog.getComments(). But wait - isn't addressing outside scopes in a CFC bad? Sure - but like most tips - there's always exceptions, and this is a good one. In this case, your remote CFC is acting as a proxy to your application-scoped code and that's a great use I think.
Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Brian Swartzfager posted on 6/21/2011 at 4:01 PM

Nice write-up, Ray: thorough yet easy enough for my team members (who are new to CFCs) to read.

Comment 2 by Stephen Duncan Jr posted on 6/21/2011 at 5:02 PM

I think you should also go over how argument passing works, and, of course, the argumentCollection trick for complex argument types: http://www.coldfusionjedi.c...

Comment 3 by Jaana Gilbert posted on 6/21/2011 at 5:03 PM

Thank you :) As always, Ray is my go-to-guy :)

Comment 4 by Raymond Camden posted on 6/21/2011 at 5:07 PM

@Stephen: I mainly wanted to focus in on the high level. That's a good additional tip though.

@Brian: You know my "ColdFusion Sample" series? I had considered doing one on CFCs. Basically taking a 2 page app (list of press releases and display of press release) with inline queries and convert it to using a CFC. I mainly wanted my "CF Sample" series to be _short_ guides, but I think I may make an exception for it.

@Jaana: Thanks. :)

Comment 5 by Michael posted on 6/21/2011 at 5:26 PM

@Ray - even in the "form mode" I have always just put the method name in the action attribute of the form tag (as opposed to a hidden form element) just like you have in your URL based example.

Either way works, but I wanted to ask if there was a benefit to doing it the way you suggested that I had never contemplated?

Comment 6 by Raymond Camden posted on 6/21/2011 at 5:29 PM

Nope, no benefit, and I too would do the same as you. In fact, when I call CFCs via jQuery, I almost always do post, but i include the "special" things in the url. So my url will be

some.cfc?method=themethod&returnformat=json

And the object parameter includes the arguments.

Now technically, together, both become one set of Arguments. But visually I like the separation.

Comment 7 by Doug posted on 6/21/2011 at 8:02 PM

Typo jumped out at me:

<blockquote>Any comments or corrections would <b>b</b>e greatly appreciated.</blockquote>

Otherwise, this is great sutff. Thnax ray!1

Comment 8 by Doug posted on 6/21/2011 at 8:02 PM

what?! No HTML?? FML

(I think I already knew that, actually.)

Comment 9 by Raymond Camden posted on 6/21/2011 at 8:04 PM

Fixed. Thanks.

Comment 10 by Jason Brookins posted on 6/21/2011 at 8:17 PM

I should know this, but you teased with this question: "isn't addressing outside scopes in a CFC bad?"

If I have some application-scope objects (like UDF or object factory) and/or request scope values for an application, how is the best way for these CFCs or simple values be used by a CFC if addressing the outside scope is a no-no?

I'm in the middle of refactoring an older application and want to make sure I don't do anything silly along the way.

Comment 11 by Raymond Camden posted on 6/21/2011 at 8:36 PM

Before I say anything, remember that "The Best Way" is _always_ subjective. My answers are always right, but for everyone else, it's subjective. (Ahem.)

So you touch on a few things here I'll try to address.

1) What about a UDF library?

I typically turn this into a CFC, normally named "utils" or something generic. I then "inject" the CFC into the CFC needing it. So my onApplicationStart may look like this (pseudo-code)

application.utils = createObject("..","utils");
application.blogService = createObject("..","blog");
application.blogService.setUtils(application.utils);

Within blogService is a simple method, setUtitls, that takes the passed in arg and sets it to the Variables scopes. Then other methods in blog.cfc can do variables.utils.whatever() Tools like ColdSpring make this a bit easier, but the principal is the same. Your blog.cfc has no idea utils _also_ exists in the app scope. It has it's own reference to it.

Um - I thought I had more to say. I don't. ;)

Comment 12 by Jason Brookins posted on 6/21/2011 at 8:55 PM

@Ray: As usual, thanks for the help. After I'm done patching some file upload security holes, I'll go update my CFCs to handle the "injection".

Comment 13 by Eric Belair posted on 6/22/2011 at 1:19 AM

@Ray, I'm a bit confused by your statement "Once you createObject the web service, you can treat it like a simple library.". Maybe I'm misunderstanding what you're trying to say, but when you call CreateObject("component", "myCFC"), isn't this simply creating a ColdFusion component object - basically, a variable that can be used in server-side logic? How can this be used as a WebService?

Comment 14 by Aaron Neff posted on 6/22/2011 at 3:46 AM

Hi Ray,

Excellent post. I just wanted to add something which is sorta related to a couple of the 'extra reading' bullet points:

It is bug #84664, which addresses an issue I ran into w/ 9.0.1's outside-web-root + onCFCRequest. I'd moved a CFC above wwwroot, then changed all access="remote" to access="public|private", then added onMissingMethod() to the CFC to map the calling of those methods. In Application.cfc, I added onCFCRequest() to intercept the requests and call the previously-remote-but-now-public|private methods via that onMissingMethod. (of course, one would want to add logic to secure access to these methods.. since what I've done essentially creates a remote bridge to public|private functions).

Well, the bug is this: If cfgrid uses "cfc:" format to bind to a non-remote function (handled within onCFCRequest), then CF throws a CFCFunctionNotRemoteException (b/c the function is non-remote).

Temp workaround is to use "url:" format.

I hope CF10 makes onCFCRequest more useful, and web service friendly (I have your #79250 in mind, which is marked as closed.. so, hopefully they've added Adam's <cfNowGoDoExactlyWhatYouWouldHaveDoneHadINotInterceptedYouAndComeBackToMeWithTheResult /> =D

Thanks!,
-Aaron

Comment 15 by Eric Belair posted on 6/22/2011 at 7:16 AM

What I mean is... CreateObject() creates an "instance" of an Object.

Comment 16 by Raymond Camden posted on 6/22/2011 at 5:34 PM

@Eric: What I meant was - if you are using a remote web service. If the CFC is local, you should NEVER use it as a web service. So for example

<cfset myws = createObject('webservice','http://some remoteurl/foo.aspx')>

Comment 17 by Eric Belair posted on 6/22/2011 at 5:49 PM

@Ray, Thank you for clarifying. I've never had the need to create a WebService Object in this way. Maybe it's a topic for a separate post, but, is there a way to use a CFC instance for AJAX calls? Basically, I have a CFC Object which I use for CRUD, as well as validation. Right now, the validation method can only be used server-side since it is totally dynamic based on other properties of the CFC. So, when I call the validation method using jQuery/AJAX ("/ext/cfc/myCFC.cfc?method=validate&returnformat=JSON") I can't initiate the other methods to set those properties necessary to runt he validation method. Does that make sense??? Any thoughts/suggestions? Thanks as always for very informative blog posts!

Comment 18 by Raymond Camden posted on 6/22/2011 at 5:51 PM

You would need to have another CFC that works with an instance.

Comment 19 by SuperAlly posted on 6/23/2011 at 6:45 AM

Nice post Ray, thanks. Explains some things I wasn't clear on.

Comment 20 by Bob posted on 5/4/2015 at 2:49 PM

Thanks for sharing this with the community, it is very informative. I have a small problem, now if I call the coldfusion component using the url, i get the whole content... meaning I get back the <cfcomponent> <cffunction> and blah, blah, blah...Any thoughts what I might be doing wrong? I do not get any errors... But If I use <cfinvoke> to call the component, it works just fine... Any thoughts will be greatly appreciated.

Comment 21 (In reply to #20) by Raymond Camden posted on 5/4/2015 at 3:30 PM

When you say you get the 'whole content', do you mean the source code?

Comment 22 (In reply to #21) by Bob posted on 5/4/2015 at 3:32 PM

Yes, that is exactly what I am getting.

Comment 23 (In reply to #22) by Raymond Camden posted on 5/4/2015 at 3:34 PM

So when you hit yourserver.com/yourcfc.cfc?... from your browser, you see code. But running yourserver.com/foo.cfm that uses cfinvoke to call it, that works?

Comment 24 (In reply to #23) by Bob posted on 5/4/2015 at 3:43 PM

Yes, it works. This is my complete scenario. I have two servers. The QA and Dev. The dev is a Windows server 2008 R2 server and is running Coldfusion 9. The QA is a Unix SunOS server running Coldfusion 9 as well. Now, when I did all my work in Dev, it works great. Calling myDEVserver.com/mycfc.cfc?m... returned the correct output and running myDEVserver.com/foo.cfm that uses cfinvoke also works. But when I moved the same two files to QA, all got messed up. Calling the myQAserver.com/mycfc.cfc?me... displays the source code instead of returning the correct output and Running myQAserver.com/foo.cfm, which uses cfinvoke, works.

Comment 25 (In reply to #24) by Raymond Camden posted on 5/4/2015 at 3:51 PM

All I can think of is that the web connector is screwed up somehow. But *normally* that would mean foo.cfm is broken too. I'd try disconnecting the web connector and reconnecting it.

Comment 26 (In reply to #25) by Bob posted on 5/4/2015 at 3:54 PM

It might be a stupid question but, how to I do disconnect a web connector and then reconnect it?

Comment 27 (In reply to #26) by Raymond Camden posted on 5/4/2015 at 3:58 PM

Look in cfinstall\cfusion\bin\connectors

Comment 28 (In reply to #27) by Bob posted on 5/4/2015 at 4:00 PM

Alright, I will contact the server admin guy. Thanks for all the help.