Twitter: raymondcamden


Address: Lafayette, LA, USA

Writing a JSONP service in ColdFusion

03-11-2009 8,307 views jQuery, ColdFusion 11 Comments

Earlier this week I blogged on JSONP (What in the heck is JSONP and why would you use it?). If that terms means nothing to you, be sure to read that article first. I thought it might be useful to talk about how you could write a ColdFusion API to support JSONP (as a producer I mean, for other people to use your API).

As we know (or hopefully now!), ColdFusion 8 made it very easy to serve JSON via Ajax. Consider the following method:

view plain print about
1<cffunction name="getPerson" access="public" returnType="struct" output="false">
2    <cfset var s = {}>
3    <cfset s.name = "Raymond">
4    <cfset s.age = "35">
5    <cfset s.suaveness = randRange(50,150)>
6    <cfreturn s>
7</cffunction>

If I wanted to expose that structure in JSON, all I'd need to do is change access to remote and specify JSON in the URL. I'd request it like so:

http://www.visitthewishlist.com/foo.cfc?method=getPerson&returnFormat=json

The returnFormat there simply asks ColdFusion to convert the structure to JSON before sending it back to the client.

Ok, so what about JSONP? JSONP stands for JSON with Padding and that presents a problem to us. We can't just return a JSON string, we have to return a JSON string plus the padding, which is a function call. ColdFusion does support a "Don't do squat with my result, just return it" version - you just change returnFormat to plain. So given that, here is how I'd build support for JSONP:

view plain print about
1<cfcomponent output="false">
2
3<cffunction name="getPerson" access="public" returnType="struct" output="false">
4    <cfset var s = {}>
5    <cfset s.name = "Raymond">
6    <cfset s.age = "35">
7    <cfset s.suaveness = randRange(50,150)>
8    <cfreturn s>
9</cffunction>
10
11<cffunction name="remoteGetPerson" access="remote" returnType="any" returnFormat="plain" output="false">
12    <cfargument name="callback" type="string" required="false">
13    <cfset var data = getPerson()>
14    
15    <!--- serialize --->
16    <cfset data = serializeJSON(data)>
17    
18    <!--- wrap --->
19    <cfif structKeyExists(arguments, "callback")>
20        <cfset data = arguments.callback & "(" & data & ")">
21    </cfif>
22    
23    <cfreturn data>
24</cffunction>
25
26</cfcomponent>

I've added a new method here, remoteGetPerson. Normally this would call an application scoped CFC, but in this case my method resides in the same file. I serialize the data and then - optionally - wrap it with the callback. Arguments.callback is expected to be a function named defined in your client side code.

Notice that I hard coded the returnFormat. In the past I've recommended against that. I've said that the caller should request a format and the CFC method should be neutral - but in this case we are specifically building in support for JSON or JSONP. I made the padding optional - well - for no good reason I guess. I noticed Yahoo supports JSONP in their requests, but allows the callback function to be optional. Since JSON is just string data, it will work fine with returnFormat=plain.

So to test this, I wrote the following incredibly interesting demo:

view plain print about
1<html>
2
3<head>
4<script src="/jquery/jquery.js"></script>
5<script>
6
7function runTest() {
8    var surl = 'http://www.coldfusionjedi.com/demos/jsonp/test.cfc?method=remoteGetPerson&callback=?'
9    $.getJSON(surl, function(data) {
10        var res = 'Name: '+data.NAME+'<br/>'
11        res += 'Age: '+data.AGE+'<br/>'
12        res += 'Suaveness: '+data.SUAVENESS
13        if(data.SUAVENESS >
100) res+=' (Rico-worthy)'
14        res+='<br/>'
15        $("#result").html(res)
16    })
17}
18
19$(document).ready(function() {
20    $("#testBtn").click(runTest)
21});
22
23</script>
24</head>
25
26<body>
27    <input type="button" value="Test" id="testBtn">
28    <div id="result"></div>
29</body>
30</html>

This is a simpler version of what I wrote for InsideRIA. I've added a simple button that, when clicked, runs a call to my CFC method. Now obviously this is in the same domain so JSONP is overkill, but in theory, folks could download the code (fix the jQuery reference - yes - I know I need to switch to Google CDN!) and still be able to run it.

Two quick notes:

1) I used jQuery in the example above, but you could use any other framework of your choosing. As long as you use jQuery. Kidding. (Not really.)

2) Using Firebug to test? (Of course you are!) I was surprised to find that these Ajax requests do not show up under XHR. I had forgotten that JSONP works by injecting script blocks into your page. Just switch to Net/All or Net/HTML and you can see the requests.

11 Comments

  • Commented on 03-11-2009 at 1:26 PM
    It would be cool if you could use OnMissingMethod() in remote calls. Then, you could have a CFC decorate the target CFC and wrap its returns in the callback method. Something like:

    data = target[ missingMethodName ]( missingMethodArguments )

    return( callback & "(" & data & ")" );

    Obviously, that syntax is not proper, but it would be a cool idea... but not possible.
  • Shiva #
    Commented on 04-30-2009 at 12:34 AM
    Very nice code. I really like to work with this but my problem is i am not able to run this in my local. In the firebug it says syntax error. I am using CF8 and i am a new be...

    var surl = 'http://www.coldfusionjedi.com/demos/jsonp/test.cfc...=?'

    when i used the above surl it is working but when i use
    var surl = 'test.cfc?method=remoteGetPerson&callback=?'

    as my cfc is in the same folder as cfm file. why it is going wrong for me? Please help me.
  • Commented on 04-30-2009 at 5:47 AM
    Any more details on the syntax error? Line #? What happens if you specify a full url for surl, ie, http://localhost/etc.
  • Commented on 08-30-2010 at 3:12 PM
    Thank you, Raymond! I've been looking all day for this -- a clear set of code that addressed what was needed on both sides of the HTML/CF implementation of JSONP AJAX style calls. Everyone has missed big chunks of how it actually works and inserted obscuring frills that just seem to hide what was really going on. Thanks again for a clear explanation and code that actually worked when I copied and ran it. Rarer than you'd think.
  • Commented on 08-30-2010 at 3:14 PM
    No problem. Glad to help.
  • Jason McNeill #
    Commented on 09-17-2012 at 2:04 PM
    @Raymond: The link in the first sentence of the first paragraph of your article is no longer live. Looks like they have archived your posts. I found the link to the archived article:
    http://www.developria.com/2009/03/what-in-the-heck...
  • Commented on 09-17-2012 at 2:04 PM
    Thanks - will edit the post.
  • Commented on 01-16-2014 at 5:31 PM
    Very, very, very cool. It works like a dream.
  • Commented on 03-11-2014 at 11:05 PM
    Another good post Ray.

    Just noticed, The link to http://www.developria.com/2009/03/what-in-the-heck... is redirecting to http://www.adobe.com/devnet.html now, I was wondering if you happened to have a copy of that post anywhere else?
    If not for me, but for others trying to figure out what the heck jsonp is too.

    Thanks
  • Commented on 03-11-2014 at 11:07 PM
    See: http://www.raymondcamden.com/index.cfm/2014/3/10/R...

    I'll republish that post - it was a good one. (I'll probably forget to mention it here, so just check the blog. Daily. Hourly! ;)
  • Commented on 03-12-2014 at 9:22 AM
    Reprinted: http://www.raymondcamden.com/index.cfm/2014/3/12/R...

Post Reply

Please refrain from posting large blocks of code as a comment. Use Pastebin or Gists instead. Text wrapped in asterisks (*) will be bold and text wrapped in underscores (_) will be italicized.

Leave this field empty