Access-control-allow-orign crosssite AJAX JSON requests. Overview !!

XMLHTTPRequest cannot load www.testexample.com origin is not allowed by Access-control-allow-orign I hope many of guys encounter this error when you do cross site AJAX request.

Well what is the need to block cross site ajax requests ?

The same origin policy has been implemented for the security reasons, since modern web apps extensively depends on the HTTP Cookies to maintain authenticated users sessions
there are possibilities that cookie/confidential data is compromised.
Hence it is needed to differentiate between the requests made by its own site and others through different domains.

more: http://en.wikipedia.org/wiki/Same_origin_policy

So how can we access json from other sites safely ?

Lets take a use case:
When i was building a RSS feed app (https://github.com/Narengowda/Feed/) where i wanted to build this app using pure JS, i ran in to these problems

1. How to access RSS (http://feeds.feedburner.com/NdtvNews-TopStories) from different sites which uses my Feed app?
2. How to convert to JSON ?

Parsing RSS XML and converting to JSON was easy tough i did it, But how to access RSS feeds ?
cant use JQuery's getJSON coz i wanted to buid Feed app using pure JS !!!

after goggling for sometime got to know about JSONP (http://en.wikipedia.org/wiki/JSONP)

The only tricky way to access JSON data is using <script> tag.
Yes the script tag loads what ever data which return from th URL which is specified in src attribute.

eg. <script src="www.test.com/json"> </script>

The very Important thing is XMLHTTPRequest to load 'scr' is not blocked eventough if the request is going cross site.

so there is a way we can load JSON somewere. yes JQuery's getJSON does the same thing.

fine how can i load XML? XML also can be loading using script tag but it leads to syntax errors...
Here i got another solution GOOGLE Feed api service (https://developers.google.com/feed/v1/jsondevguide) using this service i was able to parse and get JSON for any RSS feed.

eg url: https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&q=http://feeds.feedburner.com/NdtvNews-TopStories&num=50&callback=callbackfunctionafterload'

the above url returns Top stories as JSON. WOW!!!

And here is the code which helps get JSON using JSONP technique or simple use $.getJSON()



/**
* Fetches feeds using google apis and JSONP.
* @param {string} i url URL to be fetched.
* @param {number} i num Number of feeds to request.
* @param {function} callback Callback to be called after json response.
*/
function getFeed(url, num, callback) {
  gurl = 'https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&q=' +
    url + '&num=' + num + '&callback=' + callback.name;
  getJSON(gurl);
}


/**
* JSONP implementation in JS.
* @param {string} i url URL to be fetched.
*/
function getJSON(url) {
  var head = document.head;
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = url;
  head.appendChild(script);
  head.removeChild(script);
}


In the URL you can see "callback=callbackfunctionafterload" what is this ??

This is the callback function which will be called when the script tag data is completely loaded, the callback function will be called with data loaded as argument to that function

and the callback will be like

function callback(data) {
  alert(data) // yey!!!  write your code to handle data.
}


Not every JSON URL's support callback feature !!
This support should be enabled from the serverside, in google feed api case it is enabled.
When you use $.getJSON() JQuery automatically appends some thing like this to the URL your are trying to access "callback=jquery3243434234", It creates some unique function so that it can call back to return data instead of loading data in script tag.

How to enable callback feature at serverside ???
I love Django :)

Here is the decorator function

courtesy : http://djangosnippets.org/snippets/2208/

from django.http import HttpResponse
from django.contrib.auth.decorators import login_required

class AllowJSONPCallback(object):
    """This decorator function wraps a normal view function                                                                                     
    so that it can be read through a jsonp callback.                                                                                            
                                                                                                                                                
    Usage:                                                                                                                                      
                                                                                                                                                
    @AllowJSONPCallback                                                                                                                         
    def my_view_function(request):                                                                                                              
        return HttpResponse('this should be viewable through jsonp')                                                                            
                                                                                                                                                
    It looks for a GET parameter called "callback", and if one exists,                                                                          
    wraps the payload in a javascript function named per the value of callback.                                                                 
                                                                                                                                                
    Using AllowJSONPCallback implies that the user must be logged in                                                                            
    (and applies automatically the login_required decorator).                                                                                   
    If callback is passed and the user is logged out, "notLoggedIn" is                                                                          
    returned instead of a normal redirect, which would be hard to interpret                                                                     
    through jsonp.                                                                                                                              
                                                                                                                                                
    If the input does not appear to be json, wrap the input in quotes                                                                           
    so as not to throw a javascript error upon receipt of the response."""
    def __init__(self, f):
        self.f = login_required(f)

    def __call__(self, *args, **kwargs):
        request = args[0]
        callback = request.GET.get('callback')
        # if callback parameter is present,                                                                                                     
        # this is going to be a jsonp callback.                                                                                                 
        if callback:
            if request.user.is_authenticated():
                try:
                    response = self.f(*args, **kwargs)
                except:
                    response = HttpResponse('error', status=500)
                if response.status_code / 100 != 2:
                    response.content = 'error'
            else:
                response = HttpResponse('notLoggedIn')
            if response.content[0] not in ['"', '[', '{'] \
                    or response.content[-1] not in ['"', ']', '}']:
                response.content = '"%s"' % response.content
            response.content = "%s(%s)" % (callback, response.content)
            response['Content-Type'] = 'application/javascript'
        else:
            response = self.f(*args, **kwargs)
        return response


Happy cross site AJAX !!