Twitter: raymondcamden


Address: Lafayette, LA, USA

Reprint: What in the heck is JSONP and why would you use it?

03-12-2014 4,117 views jQuery, JavaScript 6 Comments
Earlier this week I blogged about the demise of DevelopRIA. Last night someone pinged me about one of my articles there so I thought it would be a good idea to republish it. The article is somewhat old (4 years or so) and doesn't mention CORS at all, but hopefully it is still useful to folks. But please keep the age in mind when reading. (My god - I actually preferred XML. What was I thinking?!?!)

Have you ever looked at some technology, or framework, and simply didn't understand why someone would use it? For some time now I've never quite gotten what JSONP is, nor why someone would use it over XML. Everything finally cleared up for me last week and since I assume (I hope!) I'm not alone in being confused, I thought I'd share what I learned.

JSONP (JSON with Padding) was created as a workaround to the cross domain problem. The cross domain problem refers to the fact that Ajax code run from website A can't access data from website B. So as a simple example, imagine trying to load Yahoo Search results from your site:

<html>

<head>
<script src="/jquery/jquery.js"></script>
<script>
$(function() {
    var req = 'http://search.yahooapis.com/WebSearchService/V1/webSearch?appid=YahooDemo&query=finances&format=pdf&output=json'
    $.getJSON(req,{}, function(data) {
        console.dir(data)
    });
});
</script>
</head>

<body>
</body>
</html>

This simple example will fail immediately with this error: Access to restricted URI denied. None of this should be new to us (although I do get asked about this every few weeks). The normal way around this is to build a proxy on your own server. So the Ajax code would hit some URL on your own domain. Server side code (like ColdFusion) handles making the remote call and then returns the results to the front end Ajax code. This works well, although it does end taking a bit longer due to the extra stops along the way. I intentionally chose Yahoo's search api for my example above since Yahoo was the first provider I noticed supporting JSON/P. At the time I had assumed it simply helped support their JSON format results. I didn't quite get what else it would provide. I figured Yahoo was simply providing JSON as an alternative to XML for data size reasons (JSON can be a lot slimmer) and that people were using it along with the "proxy" setup I just described.

Last week I came across this excellent article at IBM: Cross-domain communications with JSONP, Part 1: Combine JSONP and jQuery to quickly build powerful mashups. Finally, everything clicked. Turns out, there is a back door to the whole cross domain issue. If you dynamically create a new script block, you are allowed to point this new script block at any domain you want. So for example, I could dynamically create a script block that acts as if I had done:

<script src="http://search.yahooapis.com/someJSLibrary.js"></script>

So with that being possible (and with the way browsers are updated this little backdoor probably won't ever be shut) you can now dynamically request dat from another server. But how do you actually work with the data? Normally script tags like the one above load a library of code into your browser. They aren't just used to load data by itself. Another problem is handling the data. If my intent was to request Yahoo search data and present it within my own UI, I'd need to be able to make the request as well as handle the result manually. This is where the whole 'with Padding' thing comes to play. An API that supports JSONP will return not only the pure JSON data you want, but will also wrap it in a function call. So in English, I can tell Yahoo: "Please return search results for 'finances'. I want the data in JSON format and I want you to wrap it in a call to a function called handleIt that I've defined below."

To see an example of this, open the following URL in your browser:

http://search.yahooapis.com/WebSearchService/V1/webSearch?appid=YahooDemo&query=finances&format=pdf&output=json

Notice the result is simple JSON. Now modify the URL a bit to request a callback:

http://search.yahooapis.com/WebSearchService/V1/webSearch?appid=YahooDemo&query=finances&format=pdf&output=json&callback=loadit

Now the JSON code is wrapped in a function call to something called loadit. If I had a function named loadit on my page, it would have been run and the search result data passed to it. To bring this together into a real working example, I'll demonstrate using jQuery. jQuery provides builtin support for JSONP. You don't have to worry about writing any of the code to inject a new SCRIPT tag into the DOM. All you end up doing is modifying the URL to tip off jQuery that a JSONP call is being made. From the docs for getJSON, we see that by ending your URL with a ?, jQuery will treat the request like a JSONP call. You don't have to specify a function name yourself. jQuery will take care of that. Here is a full example:

<html>

<head>
<script src="/jquery/jquery.js"></script>
<script>
var baseurl = 'http://search.yahooapis.com/ImageSearchService/V1/imageSearch?appid=YahooDemo&output=json&query='

function search() {
    var search = $("#search").val()
    console.log(search)
    var surl = baseurl + escape(search) + '&callback=?'
    $.getJSON(surl, function(data) {
        var res = '<h1>Search for '+search+'</h1>'
        res += '<p>There were '+data.ResultSet.totalResultsAvailable+' results.</p>'
        for(var i=0; i<data.ResultSet.Result.length; i++) {
            var result = data.ResultSet.Result[i]
            var resultStr = '<img src="'+result['Thumbnail']['Url']+'" align="left">';
            resultStr += '<a href="'+result['ClickUrl']+'">'+result['Title']+'</a><br clear="left"/>'
            res+=resultStr
        }
        $("#result").html(res)
    })
}

$(document).ready(function() {
    $("#searchBtn").click(search)
});

</script>
</head>

<body>
    <input type="text" name="search" id="search"> <input type="button" value="Search" id="searchBtn">
    <div id="result"></div>
</body>
</html>

I built a simple form and tied it my search function. I had created a base URL for Yahoo so that all I need to do is append the current search term from the from. After that I added callback=?. Again - this tips off jQuery that it needs to treat the request like JSONP. I can then supply an inline function to handle the results. I won't go into detail about the code there as it is mostly HTML handling, but I think you get the idea.

Interesting technique - but would I use it? First, you can only use it with APIs that support JSONP (anyone have a good list of them?). Secondly, even if an API does support JSONP, you may still prefer the proxy support for a few reasons. First, it may be easier to massage the results on the server side. You may want to filter, for example, the search results to items that match with certain products in your database. That could be done with further Ajax calls, but it may be simpler to just do one call to your server and let it do all the handiwork. Using a proxy also makes it a bit easier to handle errors. Again, you could do that client side, but you may want to revert to server side cached results in the case where Yahoo fails to respond.

I'd love to hear from people using this in production!

6 Comments

  • Michael Schmidt #
    Commented on 03-12-2014 at 11:44 AM
    I have used JSONP for receiver a twitter feed, in working with Google GeoCoding, and working with my own custom methods across domains.

    JSONP is powerful and I use it, but if someone is worried about security they might look into sandboxing their JSONP calls, by putting them into a sandboxed iframe that you can then use postMessage to communicate with the parent frame.

    I also have the warning for server side developers to only expose things via JSONP that are OK to be retrieved anywhere.
  • Martin Cisneros #
    Commented on 03-21-2014 at 2:04 AM
    I use it to save data from a phonegap app to a local server... is there another way to post the data to another server without CORS?

    I POST my data using the GET in a JSONP call (haters gonna hate), theres a limit of how much GET requests you want to do in one step so i divided my data and made various JSONP and everything is fine
  • Commented on 03-21-2014 at 6:20 AM
    "I use it to save data from a phonegap app to a local server"
    You know you don't need CORS or JSON/P for PhoneGap, right? It automatically removes XHR security stuff. (Although you need to ensure your access block in config allows the domain.)
  • Michael Schmidt #
    Commented on 03-21-2014 at 8:38 AM
    Even in a website where you have control of both sides of the website the front end and the back end but they live on different domains you can have a form post to a hidden iframe that you then can communicate via post message between the iframes on the different domains.
    There is no need to have to do huge transactions over get requests.
  • Commented on 03-21-2014 at 8:42 AM
    Or just use CORS. ;)
  • Martin Cisneros #
    Commented on 03-22-2014 at 1:17 AM
    Raymond Camden: Thanks a lot for the tip hehe, now i changed my code and now is much better :)

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