September 1, 2014

JSON to JSONP bypass Same-Origin Policy

บทนำ
บทความเก่าของเจ้าของบทความ (Prepare a JSON Web Service and access it with JQuery), ได้อธิบายเกียวกับ JSON, วิธีที่จะ return JSON data จาก Web Service, และวิธีใช้ JSON-enabled Web Service โดย JQuery. โดยในบทความนี้, จะอธิบายวิธีใช้งาน JavaScript Object Notation with Padding (JSONP), ที่เป็น extended concept จาก JSON, สามารถถูกใช้งานเพื่อแก้ปัญหาสำหรับ cross domain.

คืออะไรและใช้ทำไม JSONP?

AJAX เป็น key technology ใน web 2.0 ที่เริ่มใช้ในวงกว้างใน web sites. AJAX ใช้ XMLHttpRequest client side API เพื่อติดต่อกับ server แบบ background. ไม่ว่าอย่างไร, สิ่งที่ไม่อนุญาตให้ทำการสื่อสาร cross domain เนื่องจากเหตุผลด้าน security. JavaScript Object Notation with Padding (JSONP) เป็นทางที่ลดช่องว่าง JSON data จาก external domains. ซึ่งเป็นวิธีที่ดีกว่าและเขาใจได้ง่ายกว่าวิธีอื่น ๆ (เช่น web proxy และ IFrame) เพื่อเอาข้อมูลจาก external domain.

Same-Origin Policy

นโยบาย Same-origin เป็นconcept ในแบบ browser-side programming languages (เช่น JavaScript) กับการอนุญาตให้เข้าถึง resources ในเว็บไซต์เดียวกัน (domain เดียวกัน) แต่ป้องกันการเข้าถึง resources จากต่าง domains. ที่เหนือกว่านโยบาย same origin, ซึ่งมีสองวิธีที่นิยมใช้กัน:

ใช้ Proxy Web Service

XMLHttpRequest (ใน AJAX) ไม่อนูุญาตให้เรียก cross domain, โดยปกติใช้วิธีการใช้ proxy Web Service เพื่อ access third party data. ถ้าคุณมีเว็บไซต์โดเมน www.mydomain.com และคุณต้องการ access data จาก domain อื่นเช่นเรียก www.thirdpartydomain.com. โดย AJAX, คุณไม่สามารถเข้าตรงจาก Web Service บน www.thirdpartydomain.com, แต่คุณสามารถเขียน Web Service จาก domain ของคุณเพื่อเข้าไปเอาข้อมูลจาก www.thirdpartydomain.com. ที่แสดงในรูปที่ 1:


Figure 1: การใช้ proxy Web Service เพื่อเข้าถึงข้อมูลจาก different domain
นี้เป็นวิธีการทำงานของ XMLHttpRequest เรียก Web Service (จาก the proxy Web Service) ใน domain ของตัวเองและจากนั้น proxy Web Service เรียก Web Service ที่แท้จริงต่อไปที different domain. แต่วิธีที่ง่ายและใช้ในวงกว้างในการแก้ปัญหา, มันใช้ Web Service สองครั้ง, ซึ่งทำให้ล่าช้า. ดังนั้นทุก ๆ การเรียก external Web Service แล้ว requires กลับมาที่เว็บ Web Service ของเรา, ซึ่งจะทำให้ server ของเลามีภาระกับ thread เพื่อ process มากขึ้น.

ใช้ IFrame

การใช้ IFrame, เราสามารถใช้ดึงข้อมูลจาก third party site. IFrame เป็นวิธีที่ง่ายแต่ยากที่จะจัดการแต่ละ IFrame ที่ไม่ขึ้นอยู่กับ element ในแต่ละ page และตอบสนองกับ IFrames ที่ยุ่งยาก. ดังนั้น, นึ่งใน content ใน IFrame ที่ถูกโหลด, โดย content ในตัวเองเป็น object เช่นเดียวกับ same-origin policy.

ทำไมเราละเมิดต้องนโยบาย same-origin?

ทุกวันนี้ web applications มีการใช้ข้อมูลเป็นจำนวนมากทั้ง data และ UI, ความซับซ้อนของ technical ที่เห็น, และรวมกับความแตกต่างของชุดข้อมูลจาก sources ที่ตางกัน. จากตัวอย่าง, คุณมีเว็บไซต์ของมหาวิทยาลัยเพื่อให้นักเรียนเข้าชม. โดย, คุณมี Facebook group ของมหาวิทยาลัยและคุณต้องการแสดงกิจกรรมของมหาวิทยาลัยภายในเว็บไซต์. เพื่อเข้าถึงข้อมูลกิจกรรมจาก Facebook, คุณต้องเข้าถึง services บน Facebook site (ที่ different domain) จากเว็บ web page ของคุณ. และนี้มาจากหัวข้อ cross domain. คุณสามารถเข้าถึง Facebook site services จาก server side code ของคุณโดยไม่ต้องใช้ cross-domain. แต่มันจะขัดขวางกระบวนการของ page จากเสร็จจนกว่า Web Service เรียกใช้จนเสร็จ. Instead ที่เรียก Web Service จาก server side code, คุณสามารถเพิ่ม JavaScript block กับการเรียก Web Service จาก client's browser และนั้นทำให้บรรเทาการโหลดตัว server. ใน Mashup application, คุณต้องการการเข้าถึง data จาก different sources. ในโลกของคอมพิวเตอร์, Mashup เป็น web application ที่รวม data หรือ functionality จากมากว่าสอง external sources.

JSONP ทำงานอย่างไร?

นโยบาย same-origin ไม่อนุญาตให้โหลด script จาก domain หนึ่งที่จัดการ properties จาก document ที่โหลดจาก different domain. browser ทำการแยก contents จาก different domains ที่ protect จากสิ่งที่ไม่เหมาสมในการจัดการ. อย่างไรก็ตามนโยบาย same-origin ไม่ขัดขวามการเพิ่ม dynamically scripts ใน page จาก domain ที่แตกต่างตราบเท่าที่ script ไม่พยายามโหลด document จาก domain อื่น. JSONP เป็นการอำนวยความสะดวก: “On-demand JavaScript”, และ “Same-origin’s เพื่อปรับจากการเพิ่ม JavaScript จาก different domains”:
On-demand JavaScript: เป็นการอนุญาตให้เพิ่ม JavaScript เข้าไปใน page หลังจาก page ถูก load เรียบร้อง. JavaScript สามารถเพิ่มและเรียก Web Service โดย AJAX/XMLHttpRequest. ดังนั้น, สิ่งที่ทำกับ on-demand JavaScript ทั่วไป, JavaScript สามารถเพิ่มหลังจาก page ถูก load. script จะถูก executed ตราบเท่าที่มันจะถูกเพิ่มเข้าไปใน page. ลายละเอียดของ on-demand JavaScript สามารถพบได้ที่นี้.
อธิบายการทำงานของ JSONP, ที่ใช้ทำงานกับ third party site www.thirdpartydomain.com และเรามีเว็บไซต์คือ www.mydoman.com, และเว็บไซต์เราจะเรียกใช้ third party site’s Web Service เพื่อ get data. มาดูรายละเอียดของลำดับขั้นตอนการทำงานของ JSONP:
  • เพิ่ม JS function เข้าไปใน page: ที่เว็บเพจ, คุณมี JavaScript function showThirdpartyData ที่รับ argument (ชนิด JSON) และ process ตัว argument นั้นเพื่อแสดง data บนเว็บของคุณ. อย่างไรก็ตาม, data จาก showThirdPartyData คาดว่า (dataArgument ใน following code snippet) ที่ตอบ argument จะมาจาก Web Service จาก different domain www.thirdpartydomain.com. ในที่นี้เป็น code ส่วนหนึ่งที่แสดง function signature:
  • <script type="text/javascript">
            function showThirdPartyData(dataArgument) { 
                //process data here
            }
    </script>
  • เพื่อความแน่ใจว่า third party Web Service รองรับ JSONP: เราต้องมี Web Service จาก www.thirdpartydomain.com ที่รองรับ JSONP. มันเป็น, Web Service ที่จะ return JSON data แต่ควรจะ packed ภายใน function name. The JSONP data จะ return จาก Web Service แบบนี้:
  • showThirdPartyData('
         { firstName: 'Sohel', lastName: 'Rana' }
    ');
    ดังนั้น, การเรียก Web Service จาก www.thirdpartydomain.com, จะ generate JSON data ที่แสดงด้านบน. การเรียก Web Service จะ generate JSON data กับ JavaScript function โดยปกติจากการเรียก. คือ function name (ภายในจะมี data บรรจุอยู่) ที่ต้องมาการครอบอยู่. ซึ่งเราต้อง pass ตัว function name เข้าไปใน querystring ในการเรียก Web Service และ Web Service will pack data ไว้ใน function name. นี้คือวิธีที่  Flickr API ทำงานและเราจะแสดงการทำงานให้เห็นในภายหลัง.
  • 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’sOnClick 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 isjsoncallback=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
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 getFlickrDataWithCatTaggetFlickrDataWithDogTag 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.getJSONmethod 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 thegetJSON 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:

สรุป

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






















This post just translate to Thai  language and share for Thai people learning json

from: http://www.codeproject.com/Articles/42641/JSON-to-JSONP-Bypass-Same-Origin-Policy

No comments:

Post a Comment