Monday, June 4, 2012

Cross domain AJAX

Same-orgin policy
The same-origin policy prevents a script loaded from one domain from getting or manipulating properties of a document from another domain.
This policy dates all the way back to Netscape Navigator 2.0.
You will meet failures while getting resource from  different host or different port or even a different port.

For example, an error will occurs when a page in domain "localhost" trying to get resource from domain "10.1.1.47". Following is the exception description in Chrome Dev tool: 
XMLHttpRequest cannot load http://10.1.1.47/diag/. Origin http://localhost:8094 is not allowed by Access-Control-Allow-Origin.

Classic cross-domain communication: suppose we have Server/Domain A, Server/Domain B. Client, usually browser load web page from server B, and it's going to get JSON data from serer A.

Solution 1: insert script element dynamically (JSONP)
This solution seems like a heterodoxy("旁門左道"), but it's the most popular and well compatible solution currently, nicely supported by jQuery API.
It works because the same-origin policy doesn't prevent dynamic script insertions and treats the scripts as if they were loaded from the domain that provided the web page.

Sample:
Server A was written in python and works on GAE. The request serves both same-domain GET request and cross-domain request, and has one parameter "y".
def makeJSONP(fn):
    def wrapped(itself):
        callback = itself.request.get("callback")
        response = fn(itself)
        if callback:
            itself.response.headers['Content-Type'] = 'application/x-javascript'
            itself.response.write( "%s(%s)" % (callback, fn(itself)) )
        else:
            itself.response.write(response)
    return wrapped

 
class GetMStat(webapp2.RequestHandler):     #support JSONP
    @makeJSONP
    def get(self):
        tyear = int(self.request.get("y"))
        yrecords = dbmodel.MonthlyStat.all().filter("ryear =", tyear).fetch(None)
        x= []
        for record in yrecords:    
            x.append(record.toJsonObject())
        return json.dumps(x) 
JSONP requests server side serving URL follow the syntax:
"url?callback=?"
And server side doesn't return the json data directly, it return slice of javascript code like "func(jsondata)", where func comes from the parameter "callback".

GetMStat is the HTTP handle code, it pass the raw json data to python decorator makeJSONP for more processes.
The decorator makeJSONP determine whether there was a "callback" in GET parameters, if there is, it return the js code style string as we mentioned just now, and set the HTTP header in response to type javascript.(otherwise, browser will give you a MIME mismatch warning)
If there was no "callback" in GET parameters, the request will be considered as request from same-domain, return the json data directly.

When client (javascript) get the response, the function "func" will be invoked with jsondata as parameter.
client B:
$.getJSON('http://localhost:8118/getmstatistics?callback=?',{y: 2012},
                    function(data){
                        alert(data);
                  
                    } //end function
                );
The jQuery API getJSON will handle the JSONP callback function without additional work.

In Chrome Dev tool, you can find the client(browser) access an URL like this:
http://localhost:8118/getmstatistics?callback=jQuery172007825652649626136_1338847580711&y=2012&_=1338847580714
The callback function name is actually the 3rd anonymous parameter in getJSON function.

Reference:
Cross-domain communications with JSONP, Part 1: Combine JSONP and jQuery to quickly build powerful mashups
jQuery API: getJSON 




Solution 2: HTTP access control (CORS) 
This solution is much easier than the JSONP solution, but works for modern browsers only. You can try it in your Chrome, not IE serial.

Check out the tech Draft from W3: http://www.w3.org/TR/cors/
And try to implement in your code, just one new line code :)
Server A
Add HTTP header "Access-Control-Allow-Origin" in JSON response.
response["Access-Control-Allow-Origin"] = "*"
That'all. It means this resource can be access by any domain in a cross-domain manner.

Reference: 
https://developer.mozilla.org/en/http_access_control

No comments:

Post a Comment