Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static JSON files over CDN via JSONP

Tags:

json

jsonp

cdn

I have a large amount of static/rarely changing data in JSON format. To improve my ASP.NET MVC application performance, I would like to move them to a CDN (Amazon Cloud Front).

However when I do that, the cross domain policy kicks in and jQuery makes a HTTP OPTIONS method call instead of HTTP GET and Amazon denies the requst with "403 Forbidden" response.

JSONP might be a way around this but since the files are static and on a CDN there is no way to wrap the JSON in a custom function. However I can recreate them wrapped with a known function name. For example:

{"LineDetails":{"LineNo":"3109","DbId":9 ....}}

I can do something like:

JsonWrapping({"LineDetails":{"LineNo":"3109","DbId":9 ....}});

The "JsonWrapping" function name will be the same for all the files.

Is it possible for jQuery to download JSON data via JSONP if it is wrapped in the same function name as shown above? My reading of jQuery JSONP is that jQuery creates some custom one time use function name for the JSONP request. Can this be overridden?

Thanks for your help.

like image 519
Mike Weerasinghe Avatar asked Jul 21 '10 05:07

Mike Weerasinghe


2 Answers

Best Practice for jQuery JSONP

In the docs for $.getJSON and $.ajax, the jsonp section notes that you can set the callback function name explicitly with the jsonpCallback config property. So if you want JsonWrapping(...) to be the function jquery expects inside of the jsonp response, you can tie things back up like so:

$.ajax({
    url: 'http://blah.com/blah.json'​​​​​​​​​​​​​​​​​​​​​​​​,
    dataType: 'jsonp',
    cache: true,
    jsonpCallback: 'JsonWrapping'
})
.done(function(r) {
    status.text('It worked.');
})
.fail(function (a, b, c) {
    status.text('It failed.');
});​​​​​​​​​​​​​​​​​​​​​​​​

In the above example the expected callback function inside the jsonp response is now JsonWrapping(), which jQuery will emit for you, respond to by calling .done() above, and cleanup after itself - much cleaner than hard-coding JsonWrapping into the page.

Dangers of Hard-Coding and Naming Suggestions

An important thing to consider is if you plan to have many jsonp calls on a single page, and your jsonp wrapping functions are hard-coded in the jsonp files, you should at least vary your wrapping function by something, like filename. Otherwise you create an asynchrony problem. For example assume you had this code:

function jsonp(url) {
    return $.ajax({
        url: url,
        dataType: 'jsonp'
        cache: true,
        jsonpCallback: 'JsonWrapping'
    });
}

jsonp('http://cdn.mine/one.jsonp')
.done(...);

jsonp('http://cdn.mine/two.jsonp')
.done(...);

One of these jsonp calls is going to finish before the other - it's impossible to know which - and jQuery is in an impossible situation where it can't know which .done() to call for which response. As a result you'll get some page loads where they're called correctly, and some where the data crisscrosses. The solution is to vary by something like filename, like:

function jsonp(url, wrapper) {
    return $.ajax({
        url: url,
        dataType: 'jsonp'
        cache: true,
        jsonpCallback: wrapper
    });
}

jsonp('http://cdn.mine/one.jsonp', 'one')
.done(...);

jsonp('http://cdn.mine/two.jsonp', 'two')
.done(...);

So the response from two.jsonp would need to look like:

two({...json object here...})

How It Works

The call at the beginning of this answer will make jQuery request the URL via GET like this:

http://blah.com/blah.json?callback=JsonWrapping

And expect this as the response:

JsonWrapping({...object here...})

I included cache: true above because this is on a CDN, and so, presumably is not meant to change very often. If you leave cache: true out, jQuery inserts a second querystring param meant for cache-busting, like:

http://blah.com/blah.json?callback=JsonWrapping&_=1365175172440

Which can ruin the point of a CDN. The goal of the second querystring parameter is to ensure the data isn't loaded from the browser cache, and, when it hits the server (the CDN in this case), the querystring is unique meaning it busts its cache as well.

Outside your scenario where you're using a CDN, there are situations where jQuery's default functionality is desirable: For example, when you want to simulate POST functionality across to another domain, without violating Same-Origin policy, jsonp with this cache busting feature can get that done for you.

If the server you're talking to expects something other than "callback" in the querystring for specifying the name of the callback function in the response, you can use the jsonp config property - for example jsonp: 'myname' would get you:

http://blah.com/blah.json?myname=JsonWrapping
like image 192
Chris Moschini Avatar answered Nov 16 '22 01:11

Chris Moschini


I just found out this is somehow possible. I solved it like so:

$(document).ready(function(){
    $.getJSON("http://example.com/staticjsonfile.json",function(data){
    //callback function isn't here
    }
});
function JsonWrapping(data){
    //It's really here
    alert(data);
}

This is not ideal, as you loose binding with the event that fired the Ajax request. So some hacking is required. However, it kinda gets the job done. I'd be very open to a better solution.

like image 29
Bart Vangeneugden Avatar answered Nov 16 '22 00:11

Bart Vangeneugden