Category Archives: AJAX

JSONP: How Does it Work?

Interestingly enough, I’ve seen a few candidates in interviews the last few months flail a bit when asked about getting around the same origin policy using JavaScript in a browser. Most know enough to toss out the term “JSONP” and perhaps another solution (e.g. proxy or CORS) but can’t really explain why one might prefer a certain solution over another. In fact, few could even describe how JSONP works aside from some JQuery library they always “just include”.

What is JSONP?

If you spend some time thinking about how JavaScript operates in a browser, you will probably arrive at the realization that the policy does not seem to effect script tags. Specifically, how many of us get JQuery from the Google CDN or some other source that is not the same origin as our current page? (I hope that answer is “most”, by the way.)

That same logic, combined with some DOM knowledge, would probably lead you to experiment with loading other types of JavaScript, aside from a library, using script tags. Since JavaScript has a global scope, if you were to reference a function that could handle that data you could then link it with your application and do something interesting with it.

JSONP is born.

The idea of JSONP is actually pretty simple: toss a script tag into the DOM with a reference to a resource that returns JSON data. Have the server return said JSON with “padding” (the “P” part of JSONP) that executes a function wrapping the incoming data. In order for this to work properly, the server API must also support JSONP. Typically, the function name is named as the callback parameter. For example, let’s pretend you are accessing a service that returns JSON weather information that looks like:

http://www.weather.com/90210

Which returns JSON like:

{"zipCode": "90210","location": "Beverly Hills","high": "85 degrees","low": "55 degrees"}

Adding JSONP support would result in a URL that looks like:

http://www.weather.com/90210?callback=MyCallback

And returns:

MyCallback({"zipCode": "90210","location": "Beverly Hills","high": "85 degrees","low": "55 degrees"});

This data is now exposed to the rest of the application as long as MyCallback is a valid function name in the global scope.

Most JSONP implementations in libraries like JQuery will automatically generate callback functions as well as cleaning up inserted script tags when the callback executes, but the basic idea is actually pretty simple.

Why To Use JSONP?

JSONP is universally supported on just about every browser and a lot of useful APIs have support for it already in place (Yahoo, Twitter, Google, etc.) Aside from the regular XMLHttpRequest and DOM differences, nothing really special needs to be done to make it work across different browsers. Finally, since it has been around for a bit, there are pretty mature JSONP plugins for just about every major library and framework around (JQuery, YUI, Prototype, etc.)

Why NOT To Use JSONP?

Deciding against using JSONP is directly related to how it works. First of all, the only HTTP method you can use is GET since that is the only method script tags support. This immediately eliminates the use of JSONP as an option to interact with nice RESTful APIs that use other HTTP verbs to do fun stuff like CRUD. And while we’re on the subject of GET, note that using anything other than URL parameters to communicate with the server API (e.g. sending some JSON over) is also not possible. (You could encode JSON as a URL parameter, but shame on you for even thinking that.)

Secondly, since you are relying on a script tag, there is no uniform way to catch errors. Some browsers will allow attaching a callback to the script tag (e.g. onError() {}), but it is not universally supported (any guesses on which browsers don’t support it?) and therefore can’t be relied upon without some unfortunate hacks. If the called API is doing syntactically correct things like returning an HTTP 404 response code when a resource cannot be found, there is no way to easily catch that error condition or parse the response body for further information. Instead, what typically takes place are APIs that return HTTP 200 status codes but contain JSON describing an error condition. There are worse things in the world, but I cringe at that approach these days.

Finally, keep in mind that JSONP is really a security vulnerability. It happens to be an extremely useful one, but the same origin policy does provide value in protecting the application and using JSONP is willfully opening up a hole that could potentially become an attack vector.

What Are Some Alternatives?

The easiest alternative is setting up a proxy to an external API through the same origin as the web application. This can be done pretty easily using tooling built into web servers like Apache and Nginx.

If you are able to remove support for IE7 and don’t care much about Opera, the end game is Cross-Origin Resource Sharing (CORS). This solution allows the full array of HTTP verbs and errors (including POSTing JSON) and has a greater level of security than JSONP. It has decent support on IE8+ (with some provisos) and is relatively easy to implement on the server. The downside is that many popular public JSON APIs today don’t yet support it.

Conclusion

CORS >= Proxy > JSONP (most of the time).

On Making Google Maps Work in IE 7

I recently spent some time last week integrating google maps into an online booking application that we are building for a client. It was my first time dealing with the Google API, and I am extremely impressed with the ease and relative cleanliness of the interface!

My main machine is a Mac, and the bulk of the programming I do is in Windows for various reasons. In order to facilitate this, I use Parallels as my Windows VM. However, for web development, there is almost no reason to be in Windows (which is one reason I enjoy it so much). I simply fire up Coda and I’m off!

Enter IE.

Yuck.

Over the weekend, I finished a test version of the site and fired off an e-mail to my boss and our client, telling them that the work was ready for review. On Monday morning, I got word that the site wasn’t working. I opened up Safari and Firefox and everything seemed to work fine. That’s when I realized I forgot to test the site in IE.

Sure enough, when I tried the site, I got the following error message:

Google Maps IE error: “A Runtime Error has occurred. Line:37.”

IE_error_gmaps.jpg

Of course, I did not get this error in IE until I enabled script debugging in the Internet Options->Advanced tab.

After doing some snooping around the Google Maps Group, I discovered that IE7, for whatever reason, did not like the version of the API. So, I changed this:

script src=”http://maps.google.com/maps?file=api&v=2&amp

to this:

script src=”http://maps.google.com/maps?file=api&v=2.50a&amp

And the error magically went away.

One problem solved. According to Google’s documentation, the “v=2” is supposed to default to the latest version of the API. However, for whatever reason, that latest version doesn’t seem to work properly with IE (works fine with Firefox and Safari, though).

One problem down.

The next problem I had was the appearance of the map in general. Instead of looking like this:

The correct map view.

Goodmap.jpg

It was coming out like this:

The IE7 map view.

Badmap.jpg

What I discovered was that the div container ID I was using for the map (simply “map”) was also the anchor link name I was using. Apparently, while Firefox and Safari can tell the difference between a link name and a div name, IE can’t. IE was, as far as I could tell, trying to fit the map into an anchor since the anchor came first (not sure I get the logic on that one).

All I had to do was change the anchor name and everything began to work as advertised.