• Cross-Domain JSON without XHR

    Aug 11 2006

    I was just reading on the Ajaxian a quote from an article Why XHR should become opt-in cross-domain, so I started thinking, isn't there a way around this already?

    A quick explanation of what I'm talking about: XMLHttpRequest (the function behind Ajax) will only let you connect to URLs on the same domain as the page you're on. This means the Ajax happening on xyz.com can only connect to URLs on xyz.com. Even if XYZ wanted to use Ajax to pull some data from foobar.com, they wouldn't be able to.

    Now there is already one popular way around this. You can make a proxy page that gets the data for you. Using the same example, XYZ could make a URL like xyz.com/json_proxy that pulled data over from foobar.com behind the scenes, thus making the foobar.com data available on xyz.com. The problem with this is, XYZ would have to handle double the bandwidth of this data unnecessarily. This can be a big problem for smaller companies.

    I have an alternative solution to this problem. Instead of using XMLHttpRequest to load the data, we can just use <script> tags instead. These don't have the same cross-server restrictions as XMLHttpRequest. In fact, this is how Adsense works. You stick a <script> tag on the page which executes JavaScript coming from Google's server, and this writes out the ads on the page.

    To add a <script> tag to our page, we only need to call this function:

    function addScript(url) {
        var script = document.createElement('script');
        script.src = url;
        document.body.appendChild(script);
    }

    If we have a URL that returns some JSON data, we just pass that URL to this function and the page will load that data. Well, not entirely. There's a few things we need to do different. With JSON, you have some data like:

    {
       "firstname": "Jesse",
       "lastname": "Skinner"
    }

    and then use XMLHttpRequest with eval() to assign it to a variable. If you just stuck a script tag on the page to load this, nothing would happen. It would just load and the JavaScript parser would say "Great, nice little piece of data." But it's not going to assign it to a variable, so you'll have no way to access it.

    There's actually no way around this unless you change what is returned by the JSON data URL. This is the special trick. We would need the JavaScript to execute some function or assign the data to a variable. It would need to look like this:

    json_callback({
       "firstname": "Jesse",
       "lastname": "Skinner"
    });

    Now, we just need to make a simple function called json_callback() that takes the data and does something with it. Voila! Cross-server JSON.

    Rather than hardcode some callback function name, it would probably be better to add an optional "callback" parameter that would specify the function name. If the callback parameter is missing, the server can return standard ol' JSON.

    Let's say our simple JSON example above came from "foobar.com/data?format=json". If example.com was kind enough to provide this flexibility, they could add the parameter so that "foobar.com/data?format=json&callback=my_json_callback" formats the data as a function call, passing the data object to my_json_callback().

    Now, we just need to wait for web APIs with JSON to start supporting this parameter.

    Update: I've since discovered that Yahoo!'s JSON API already provides a 'callback' parameter exactly as I've described above, and I suspect that other APIs out there may support this as well. Either I'm psychic or there are time travellers working at Yahoo! ;) Here are the details from Yahoo!

  • Comments

    1. Peter Nixey at 6:51am on August 11, 2006

    Hi Jesse,

    Thanks for your response to my article and your technique is spot on. The issue I was highlighting though is that whilst cross-domain JSON is a solution to the data-access problem, it's very insecure and dangerous.

    We can get it but we have to give the other server complete access to our webpages in the process.

    2. Jesse Skinner at 7:03am on August 11, 2006

    Hey Peter,

    Yes, I agree, this method is certainly not secure. In a sense, allowing the 'callback' parameter is similar to giving a header of 'Cross-domain-access: true'. As with the header, you would have to be careful which data you exposed cross-domain. del.icio.us bookmarks might be okay, but I sure wouldn't want my gmail inbox exposed.

    3. Pat at 12:18am on June 25, 2008

    Dude, This article saved my life.

    I ended up inserting my .js files using this code:

    var headElement = $doc.getElementsByTagName('head')[0];
    var script = document.createElement('script');
    script.language="javascript";
        script.src = url;
        headElement.appendChild(script);

    Later I referenced them using:

    return $wnd[varName];

    This is all probably pretty simple stuff to javascript experts.

    4. Chris at 8:37pm on July 14, 2009

    For anyone stumbling across this article, Google's json also supports a callback
    http://code.google.com/apis/gdata/json.html#Request

    5. Yogi at 2:04pm on September 2, 2009

    This approach uses "GET" to get content. Since browser has some limitation on URL length using get, it fails for larger data. Can we some how use POST to achieve same result.

    6. Jesse Skinner at 2:11pm on September 2, 2009

    @Yogi - Long story short, no you can't.

    You can submit a form to another domain using a hidden iframe, but you cannot get a response back from that domain (or see if it fails), which may work for some purposes.

    Alternatively, you can try splitting up the data into multiple requests, each under 2kb or 4kb or whatever URL limit you're trying to stay within.

    7. dm at 7:40pm on January 9, 2011

    "If you just stuck a script tag on the page to load this, nothing would happen. It would just load and the JavaScript parser would say "Great, nice little piece of data." But it's not going to assign it to a variable, so you'll have no way to access it."

    are you able to provide an example or url demonstrating how to achieve this?  that would be greatly appreciated...

    8. Jesse Skinner at 5:21am on January 10, 2011

    @dm - The example above shows how it's achievable, using a callback function. In the paragraph you quoted, I explain why it's not achievable in general.

    9. lucky at 12:56am on March 1, 2011

    Very great Explanation really useful..And one more thing is there any way of checking the response status in this way od using dynamic script tag.

    10. lucky at 12:58am on March 1, 2011

    I simply want to know whether i got success response or failure...and i need to do some implementation on success but, dont want to do any thing with responseText

    11. lucky at 12:59am on March 1, 2011

    Is there any way to find what kind of response status we got

    Commenting is now closed. Come find me on Twitter.