Introduction JSON
In my last article (Prepare a JSON Web Service and access it with JQuery), I had explained JSON, how to return JSON data from a Web Service, and how to use a JSON-enabled Web Service with JQuery. In this article, I will explain how JavaScript Object Notation with Padding (JSONP), an extended concept from JSON, can be used to solve cross domain issues.
What and why JSONP?
AJAX is a key technology in web 2.0 that is being used widely in web sites. AJAX uses the XMLHttpRequest
client side API for communicating with the server in the background. However, this approach doesn't allow cross domain communication due to security reasons. JavaScript Object Notation with Padding (JSONP) is a way to grab JSON data from external domains. It's a better and cleaner alternative to other approaches (web proxy and IFrame) to get data from an external domain.
Same-Origin Policy
Same-origin policy is a concept in browser-side programming languages (such as JavaScript) which allows accessing resources in the same site (same domain) but preventing accessing resources in different domains. To overcome the same origin policy, we have the two following popular options in our hand:
Use a Proxy Web Service
As XMLHttpRequest
(in AJAX) doesn't allow cross domain calls, the common approach is to use a proxy Web Service to access third party data. Say your site is hosted in www.mydomain.com and you need to access data from a different domain called www.thirdpartydomain.com. With AJAX, you can't directly call a Web Service on www.thirdpartydomain.com, but you can write a Web Service in your domain which will get data from www.thirdpartydomain.com. The approach is shown in figure 1:
This approach works as the XMLHttpRequest
calls the Web Service (i.e., the proxy Web Service) in my own domain and then the proxy Web Service calls the actual Web Service on the different domain. But though this is the easiest and widely used solution, it requires two Web Services calls, which is slower. Also, every call to the external Web Service requires going through my Web Service, which takes my server's valuable thread to process.
Use IFrame
Using IFrame, we can easily fetch data from a third party site. IFrame is easy to use but difficult to manage as each IFrame is an independent element in a page and interactions between IFrames are difficult. Also, once the content in the IFrame is loaded, the content itself is subjected to the same same-origin policy.
Why do we need to break the same-origin policy?
Today's web applications are rich in both data and UI, complex from a technical point of view, and combine different sets of data from different sources. For example, you have a university site where students visit. Also, you have a Facebook group for this university and you want to show group activities in your university site. To get activities from Facebook, you need to access services on the Facebook site (a different domain) from you web page. And here comes the cross domain issue. You can access Facebook site services from your server side code without any cross-domain issue. But this will prevent the processing of the page from finishing until the Web Service call finishes. Instead of calling the Web Service from server side code, you can add a JavaScript block which call the Web Service from the client's browser and thus alleviate load on your server. In a Mashup application, we need to access data from different sources. In the computing world, Mashup is a web application that combines data or functionality from two or more external sources.
How JSONP works?
The same-origin policy doesn't allow a script loaded from one domain to manipulate properties of a document loaded from a different domain. The browser does so to isolate contents from different domains to protect from improper manipulation. However the same-origin policy doesn't prevent adding scripts dynamically in the page from a different domain as long as the script doesn't try to load document from a different domain. The JSONP is a combination of these facilities: "On-demand JavaScript", and "Same-origin's flexibility for adding JavaScript from different domains":
On-demand JavaScript: This allows adding JavaScript to the page after the page is loaded. The JavaScript can be added by calling a Web Service with AJAX/
XMLHttpRequest
. So, due to this on-demand JavaScript facility, JavaScript can be added after the page is loaded. The script will be executed as soon as it will be added to the page. Details on on-demand JavaScript can be found here.
To explain how JSONP works, consider there's an external third party site www.thirdpartydomain.com and we have our site www.mydoman.com, and our site will call a third party site's Web Service to get data. Let's consider the following scenario to explain JSONP:
- Add a JS function in your page: Say, in your page, you have a JavaScript function
showThirdpartyData
which takes an argument (of type JSON) and process this argument to show data on your site's web page. However, the datashowThirdPartyData
expects (dataArgument
in the following code snippet) that ans argument will come from a Web Service on a different domain www.thirdpartydomain.com. The following code snippet shows the function signature:
<script type="text/javascript"> function showThirdPartyData(dataArgument) { //process data here } </script>
showThirdPartyData('{ firstName: 'Sohel', lastName: 'Rana' }');
So, by calling the Web Service on www.thirdpartydomain.com, we will generate JSON data as shown above. Calling the Web Service will generate JSON data which is actually a JavaScript function call. The function name (in which data will be packed) needs to be configurable. Say, we can pass the function name in the querystring in the Web Service call and the Web Service will pack data inside the function name. That's how Flickr API works and we'll see that later.
callWebService
) on a button's OnClick
event:function callWebService() { // Insert dynamic script var script = document.createElement('script'); script.src = 'http://www.thirdpartydomain.com/webservice/... '; // append the script in the document body. // As per on-deman script behaviour as soon as you add the script to // the document,the script will be execute and the web service will // be called. document.body.appendChild(script); }
Here is the sequence of how JSONP will work:
- The
callWebService
method will add a script tag in the page and will set the source of the script tag to the URL of the third party Web Service. - You will call the
CallWebService
method on some events, say on a button's click event. As soon as thecallWebService
method is called, the script from the third party site will be added in the page. - As soon as the script tag is added to the page, the script will be executed as per on-demand JavaScript's behavior. Once the script is executed, the method
showThirdPartyData
will be called in the page.
How a Web Service can be JSONP compatible
For JSONP to work, the Web Service from www.thirdpartydomain.com will support JSONP. To support JSONP, the Web Service must return JSON data in the format:
functionName('{JSONData}')
Here the function name is a JavaScript function name. So when you call the Web Service from another site, the Web Service will generate JSON data which is actually a JavaScript function call. For example, to get the latest images tagged as 'cat' from Flickr, we can call the Web Service as: http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=myFunctionName.
In the above URL, the querystring tags=cat tells the service to return images tagged with cat. Here, the important one is jsoncallback=myFunctionName. This tells the service that the return data will be packed in a function call named myFunctionName
. If you hit the URL, you will get the JSON data as shown below:
So the above call to the Flickr API takes the function name in the querysting jsoncallback
and we can pass the method name we are interested to use.
A real life example with Flickr
In the code provided along with this article, I have used the Flickr API to show recent images. Using JSONP, I have accessed data from flickr.com which is different from my domain. The Web Service URL is: http://api.flickr.com/services/feeds/photos_public.gne?tags=dog&tagmode=any&format=json&jsoncallback=showFilckrDataDog.
When you run the application provided along with this code, you will get two buttons in a web page. Clicking on a button will show the images tagged with cat, and clicking on another will show images tagged with dog. The two buttons invoke two different JavaScript functions: getFlickrDataWithDogTag
and getFlickrDataWithCatTag
. getFlickrDataWithDogTag
uses the native JavaScript approach to add a script to the page. Whereas getFlickrDataWithCatTag
uses JQuery to call the JSONP supported Web Service. The JQuery.getJSON
method allows to load JSON data from a different domain. The format of the getJSON
method is:
jQuery.getJSON(url, data, callback)
url
is the URL for the Web Service. data
(which is JSON data to the Web Service) can be omitted if not needed. callback
is the callback method which will be called once the JSON data will be returned from the Web Service. Remember when you call the Web Service with the getJSON
method, the Web Service URL will be a bit different. The jsoncallback
querystring needs to have a '?' value. The URL to be used in the getJSON
method is shown below:
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?" + "tags=cat&tagmode=any&format=json&jsoncallback=?", showFilckrDataCat);
jQuery will automatically replace the '?' with the callback method (in the above URL, this is the showFlickrDataCat
). And also, jQuery will add the script tag for you. So, when the Web Service call will return, jQuery will add the script tag which will invoke the JavaScript method in your page.
No comments:
Post a Comment