Coding with Jesse

Cross-Domain JSON without XHR

August 11st, 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!


Interested in web development? Subscribe to my newsletter!

Comments

1 . Peter Nixey on August 11st, 2006

Peter Nixey

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 on August 11st, 2006

Jesse Skinner

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 on June 25th, 2008

Pat

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 on July 14th, 2009

Chris

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

5 . Yogi on September 2nd, 2009

Yogi

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 on September 2nd, 2009

Jesse Skinner

@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 on January 9th, 2011

dm

"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 on January 10th, 2011

Jesse Skinner

@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 on March 1st, 2011

lucky

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 on March 1st, 2011

lucky

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 on March 1st, 2011

lucky

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

Comments are closed, but I'd still love to hear your thoughts.