Anyone who develops Javascript long enough undoubtedly runs into difficulties involving the various security features all browser vendors implement. These security features are a good thing -- they protect us from malicious users hijacking our browsing experience. But they can certainly cause some headaches. The security feature that presents the most difficulty for us as developers is the same origin policy.

In a nutshell, this policy prevents pages from two different domains from modifying each others properties, using XMLHttpRequest, setting cookies etc. For instance, Example.com and OtherExample.com can't get references to each others document properties and can't set cookies on each other. Additionally, Example.com can't use XMLHttpRequest (aka AJAX) to load a resource from OtherExample.com. This last bit is probably the biggest issue for developers today -- in todays world of open web services and mashups. How do you consume a web service with Javascript if you can't load the data properly?

Solution 1: Use a server-side proxy

The first way around this problem is to use a very simple server-side script that acts as a proxy to the web service. So instead of requesting AJAX from the remote site, you request it locally on your own domain through the proxy. The proxy itself has it's programming logic to send your request off to the remote site, gather the data, and then serve it back to you.

.________________________.
| Client on YourSite.com |
'-\/---------/\----------'
  ||         ||
  ||_________||__.
  | AJAX Request |
  '---\/-----/\--'
      ||     ||
      ||_____||______________.
      | YourSite.com         |
      | (Ex. ajax_proxy.php) |
      '---\/-----/\----------'
          ||     ||
          ||_____||__________.
          | OtherSite.com    |
          '------------------'

Since your server-side script has no qualms about fetching data from another domain, you can successfully proxy all AJAX like this without running into any trouble. And then since the client browser is fetching the data from the local domain (even though you're making another request behind the scenes), it doesn't violate the same origin policy.

An example proxy script might look something like this (maybe a little more complex if you're handling POST data too):

  1. $url = 'http://othersite.com/someservice?' . http_build_query($_GET);

Remember, the sole purpose of the proxy is just so the client browser can load the data locally on the same domain so the same origin policy isn't violated.

Drawbacks

This method works well but has two obvious drawbacks. First is of course that you need a server-side script at all. Especially if you are providing a service, this raises the barrier for entry and makes it harder for "noobs" to use your widget -- it's not a simple "paste this code into your footer" instruction; you also need to explain how to install the proxy service.

The second drawback is that your own servers are making these requests which makes the whole process slower, but also eats up your resources -- both your processing power and even just simple bandwidth.

Solution 2: JSONP

The second solution is to use JSONP -- "JSON with Padding." This is a technique that lets you get around the same origin policy. In order to use JSONP, the service you are requesting needs to support it.

So in the last solution, the consumer (the user using the service) required an ajax_proxy.php or whatever server-side proxy script. In this solution, the provider (Digg, Amazon, Yahoo -- whatever) needs to support JSONP themselves. Thankfully, these days the idea is catching on and it's likely JSONP is an option. And if you're a service provider, then you'll want to build it in to your service for sure.

How it works

A normal JSON request is sent using XMLHttpRequest and the reply looks something like this:

Plain TextJAVASCRIPT:
  1. {'uid': 23, 'username': 'Chroder', 'name': 'Christopher Nadeau'}

Now the truth is, JSONP isn't AJAX at all technically speaking because it does not use XMLHttpRequest. JSONP requests are made by dynamically inserting a <script> tag into the DOM. For example, you'd insert this:

  1. <script type="text/javascript" src="http://othersite.com/service?all=your&params=as&usual=gohere"></script>

Then that remote Javascript file is loaded and contains an actual Javascript function call. For example:

Plain TextJAVASCRIPT:
  1. handleJsonReply({'uid': 23, 'username': 'Chroder', 'name': 'Christopher Nadeau'});

In other words, instead of reading AJAX data directly into a variable in Javascript, you define a callback function that is called when the data arrives, and the first parameter is the data itself as an object literal. If you were a provider implementing JSON on your service you might have something like:

  1. // A service provider accepts a 'callback' parameter
  2. // that it uses to wrap an AJAX reply
  3.  
  4. // Imagine we did some work here
  5. $json = json_encode($mydata);
  6.  
  7. // Using JSONP
  8. if ($_GET['callback']) {
  9.     echo $_GET['callback'] . "($json);"; // somefunction({data here});
  10.    
  11. // Normal JSON
  12. } else {
  13.     echo $json;
  14. }

The Javascript source code result of that request is nothing but a real executable function call with a Javascript object literal. It's as if you added the tag yourself and it called some functions -- the difference being that the JS is written by the producer on-the-fly and embeds the data you want right in the code. This is why it all works; it's not actually AJAX at all, it's just loading a remote Javascript file that happens to have some useful data in it.

Using a library like jQuery that supports JSONP, these details of inserting the special script tag and creating the special callback function are all taken care of automatically. Using a JS library, usually the only difference between JSONP and real AJAX is that you enable a 'jsonp' option.

Drawbacks

The first limitation to this method is that you have to rely on the provider to implement JSONP. The provider needs to actually support JSONP -- they need to wrap their JSON data with that callback function name.

Then the next limitation -- and this is a big one -- is that JSONP doesn't support POST requests. Since all data is passed in the query string as GET data, you are severely limited if your services require the passing of long data (for example, forum posts or comments or articles). But for the majority of consumer services that fetch more data than they push, this isn't such a big problem.

JSONP with jQuery

Step 1: Make sure the provider supports JSONP.
Step 2: Set the dataType option to jsonp, and if the provider uses a different GET param other than 'callback', specify the jsonp option to that parameter name.

Plain TextJAVASCRIPT:
  1. $.ajax({
  2.     // ... Use the AJAX utility as you normally would
  3.     dataType: 'jsonp',
  4.     // ...
  5. });

jQuery will generate a unique callback name for this request (something like json1268267816). Thus, the reply from a web service would be something like:

Plain TextJAVASCRIPT:
  1. json1268267816({'uid': 23, 'username': 'Chroder', 'name': 'Christopher Nadeau'});

But jQuery handles it all seamlessly, so you as the developer just handle it like a normal AJAX request using the same jQuery success/failure/complete callback hooks.

4 Responses to “Cross-domain AJAX with JSONP”

  1. Chainat Says:

    It's the best article about JSONP I have read in the last two days. It's easy to read and covers the main important points. Not many articles out there mention how the JSONP works in background in the way this article does. Even json.org doesn't do a good job on this item.

    I like this paragraph:

    Now the truth is, this isn't really AJAX at all technically speaking. AJAX is usually defined by the use of the XMLHttpRequest object. JSONP, however, works by dynamically loading a new tag into your page

    Thanks,

  2. droope Says:

    Hi!

    Interesting read. Not sure if I got it, though...

    you say:

    echo $_GET['callback'] . "($json);"; // somefunction({data here});

    So that would mean, if we did a $.get request with jquery - for example -, that the data argument of the callback function would be "somefunction({data here});", right?

    What would we do with that? If we eval it, then "somefunction" would be called, i'd think. Where/by who is that defined?

    Thanks! i think it's a very interesting subject :) I'll be sure to follow this blog. ( you can follow mine, if you understand spanish :P )

    Regards,
    Droope

  3. Christopher Says:

    jQuery takes care of writing somefunction() automatically (it's a randomly generated name). So it's a low-level detail that is hidden away for you for convenience.

    If you did this "manually," basically you'd define the function before hand:

    function myjsonpfunction(data) {
        mycallback(data); //pass on to your "real" callback
    }
    

    And then to make your AJAX request, you'd manually modify the DOM to insert a script tag

    <script type="text/javascript" src="http://othersite.com/service?&callback=myjsonpfunction"></script>
    

    Which, when loaded, contains an actual Javascript function call like:

    myjsonpfunction({ data: 'from', the: 'web', service: 1 });
  4. droope Says:

    Ahh!! thanks :)

    I get it now.

    This is a very good resource to read more about this (and about anything javascript related):

    http://www.hunlock.com/blogs/Mastering_JSON_(_JavaScript_Object_Notation_)

    Regards,
    Droope

Leave a Reply