Blog

11
March 2014

Gavin Pickin

Mobile Development - JSON vs JSONP with Ajax calls and ColdFusion CFC Components

Android, CFML Language, Cordova / Phonegap, IOS, jQuery, Techie Gotchas

I had mentioned that I am getting setup and writing some HTML5 / Javascript / CSS / Phonegap / Cordova Apps, and along with these types of apps, you will start to use Ajax to make server api calls to keep your fresh and up to date. I ran into a hurdle today when making some Cross Site Ajax calls, and decided I would share my findings.

First, you might ask, if I'm making a native app, why are you worrying about Cross Site Ajax calls? And you would be correct, with the app deployed on a Android or IOS device, those Cross Site Scripting issues do not exist. I decided I wanted to develop my apps to be deployed on our dev servers during development and testing, and wanted them to work as well as they could (obviously certain device api's would not be available), so I wanted to look into using JSONP to solve my cross site scripting issues. Using json a lot, and with most of my sites calling the same server for api calls, I have heard of JSONP, but not actually implemented it myself… so here goes.

Why JSONP, or maybe What is JSONP?

EDIT: Ray Camden just reposted an old article that had been removed, that might explain it better than me. Thanks Ray for posting it again. 
http://www.raymondcamden.com/index.cfm/2014/3/12/Reprint-What-in-the-heck-is-JSONP-and-why-would-you-use-it

JSONP is basically a workaround that bypasses cross domain scripting policies. Instead of making an ajax call and requesting json, you actually create a script tag, with the get call as the SRC attributes of the script tag, which is allowed to call across domains, the difference is, instead of JSON, you get json wrapped in a function call, so when the script tag completes, it runs the call which basically is passing the json you wanted in the first place. Instead of returning an object that looks like this

{ "Name": "Gavin", "Height": "6'5" }

 

You would get something like this

callBackFunction({ "Name": "Gavin", "Height": "6'5" });

 

Sounds complicated? It is, but guess what, jQuery makes it easy… because all you do is change the dataType from json, to jsonp and jQuery behind the scenes creates the script tag, creates a callback function, appends the name of the callback to your ajax url, and all you have to do it make sure the server you are hitting returns jsonp, and not json.

What does the javascript look like for a same site json request

$.ajax({
    url : "http://servername.com/cfcs/User.cfc?method=getUser&user="+userid,
    type : "GET",
    dataType: 'json'})
   
    .done( function(data){
         alert('success');
     });

What does the javascript look like for a cross site jsonp request

$.ajax({
    url : "http://servername.com/cfcs/User.cfc?method=getUser&user="+userid,
    type : "GET",
    dataType: 'jsonp'})
   
    .done( function(data){
          alert('success');
    });

  

Yup, thats it, just need to change the json to jsonp… and jQuery does the rest.

 

I setup my cfc to get the call, and return my json, and I got a strange error. 
In Chrome, the message was this if I passed back returnformat="plain" and returntype="string"

Resource interpreted as Script but transferred with MIME type text/plain: "http://servername.com/cfcs/User.cfc?method=getUser&userid=1234&callback=jQuery110207742651014123112_1394599853046&_=1394599853047". jquery-1.10.2.js:8516

Uncaught SyntaxError: Unexpected token :

In Firefox, the message was this

SyntaxError: missing ; before statement

 

Now, if i set returnformat="json" and returntype="string" , the ajax call still fails, but the console has no error messages at all from Chrome or Firefox.
If I added a .fail into the ajax call, I can see there is a parse error, but it was not the easiest thing to debug. This is one of the big issues with jsonp… because you're abstracting the actual call behind this callback, the debugging is not as easy to follow, and just isn't as helpful as it might be in a normal json call. 

I thought maybe I had invalid JSON, maybe I had invalid characters like Ben Nadel had mentioned in a post. 

I knew that jQuery did all the work, but what I didn't realize was my CFC wasn't passing back a jsonp response, and although its obvious now, at first, debugging it was really pretty hard. The fact I didn't know that the jsonp response had to be constructed differently was my mistake, I guess I assumed jQuery was so awesome, it could fix my server side code too. As you can tell, from above, the error messages weren't obvious… so that is why I wanted to share my experience.

I modified my cfc function from this

<cffunction name="getUser" access="remote" returntype="string" returnformat="plain" output="false">
    <cfscript>
        if ( structKeyExists(url, "agentid")) {
            return '{"Code": "200", "ID": "12345"}';
        }
        else {
            return '{"Code":"400","ID":"None"}';
        }
    </cfscript>
</cffunction>

 

to this

<cffunction name="getUser" access="remote" returntype="string" returnformat="plain" output="false">
    <cfscript>
        if ( structKeyExists(url, "agentid")) {
            return '#arguments.callback#({"Code": "200", "ID": "12345"});';
        }
        else {
            return '#arguments.callback#({"Code":"400","ID":"None"});';
        }
    </cfscript>
</cffunction>

 

And it works.
You can see the in one of my error messages, you see the full url.

http://servername.com/cfcs/User.cfc?method=getUser&userid=1234&callback=jQuery110207742651014123112_1394599853046&_=1394599853047

jQuery had created this callback function called jQuery110207742651014123112_1394599853046 and added that to the url as the value to the callback key pair. To respond with jsonp instead of json, I simply needed to respond with a valid line of javascript, to be executed like any other line of javascript, which calls a method, by the name of the value of the callback passing in 1 variable, the json response, and ending it with a semi colon for good measure.
So my page should return

jQuery110207742651014123112_1394599853046( {"Code": "200", "ID": "12345"} );

When this returns, the page executes this function… which passes the data to my success function, and nothing else on the client side had to change.

Remember, this is not json, its a line of code, so your function should return format="plain" not json. This obviously means that you cannot use ColdFusion's built in serialization by using the returnformat="json" when you need to return jsonp, so you might want to call a second function for jsonp calls, that just wraps your normal function with a jsonp function.

<cffunction name="getUserP" access="remote" returntype="string" returnformat="plain" output="false">
    <cfreturn '#arguments.callback#( #getUser(argumentCollection=arguments)# );'>
</cffunction>

 

Then when you want json, call getUser, and when you want jsonp call getUserP. That way the mime types match, and you can make use of the normal return format="json" for your normal method, and you're keeping to the DRY Don't Repeat Yourself rule at the same time.

 

Hope this helps someone looking at JSONP for the first time, thanks for reading,

Gavin

by mihiran
12/09/2014 09:57:43 PM

hi gavin, jsonp is not working in mobile browser. computer it works. please help. Thank You!

by MD
01/14/2015 09:11:10 AM

Thanks for posting. This is helping, but how do I parse that JSONP result when it comes back? I can see that format in the Response tab of FireBug, but how do I work with the individual variables in that response? For example, what if I wanted to do an alert(ID); ?

Blog Search