Click here to Skip to main content
Click here to Skip to main content

JSON to JSONP: Bypass Same-Origin Policy

, 26 Sep 2009
Rate this:
Please Sign up or sign in to vote.
This article shows how JavaScript Object Notation with Padding (JSONP) can be used to get data from different domains, bypassing same-origin policy.

Introduction

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:

Fig_1_-_Proxy_to_access_third_party_domain_data.jpg

Figure 1: Using a proxy Web Service to access data from a different domain

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 data showThirdPartyData 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>
  • Make sure the third party Web Service supports JSONP: We need to have a Web Service on www.thirdpartydomain.com which will support JSONP. That is, the Web Service will return JSON data but should be packed within a function name. The JSON data the Web Service will return will look like:
  • 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.

  • Call the third party Web Service: Now you can call the www.thirdpartydomain.com Web Service with AJAX as it generates a JavaScript function call and the JavaScript function doesn’t try to access data from a different domain except www.thirdparty.com. So, calling this Web Service complies with the same-origin policy [the same-origin policy doesn’t prevent adding a script dynamically in the page from a different domain until the script doesn’t try to load the document from different domains]. The following code block can be used to call a Web Service from the www.thirdparydomain.com site. You can call the following JavaScript function (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:

  1. 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.
  2. You will call the CallWebService method on some events, say on a button's click event. As soon as the callWebService method is called, the script from the third party site will be added in the page.
  3. 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:

Fig_2_-_JSONP_support_in_flickr_API.jpg

Fig 2: JSONP support in Flickr API

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.

References

You can get more information about the JSONP in the following URLs:

Conclusion

For applications like Mashups, we need to combine data from different sources which may not be on the same domain. Same-origin policy prohibits accessing data from other domains. We have a few choices to bypass the same-origin policy. One solution is a proxy Web Service which puts unnecessary processing whereas each individual IFrame has the same-origin policy restriction. JSONP provides a better alternative to the other two approaches. However, we need to have JSOP supported Web Services on the other end to consume data with JSONP.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Sohel_Rana
Architect ImpleVista Aps
Denmark Denmark
Sohel has more than six years of experience in professional software development with extensive involvement in Web based Object-Oriented, Multi-Tiered application design and development. He's Familiar with Test Driven Development (TDD) and refactoring techniques as well as having expertise in architecturing large enterprise applications. He has Experience in working with Content Management System and Portal Management System tools like SharePoint, DotNetNuke, Ektron.
 
Over last few years, he’s involved in development with projects on Microsoft SharePoint and received Microsoft MVP for SharePoint Server Development in the year 2011 and 2012. Currently he's working in a software company located Copenhagen,Denmark on a project integrating SharePoint and SAP. You can read his popular blog at: http://ranaictiu-technicalblog.blogspot.com

Comments and Discussions

 
GeneralMy vote of 5 PinmemberOpa Knack3-May-11 4:41 
GeneralExcellent article PinmemberEchilon27-Sep-09 9:16 
I've never really read in to JSONP, but this article makes it really easy to grasp. Thanks!
GeneralRe: Excellent article PinmemberSohel_Rana27-Sep-09 16:39 
GeneralRe: Excellent article PinmemberDokMuffin28-Sep-09 6:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 26 Sep 2009
Article Copyright 2009 by Sohel_Rana
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid