The script tag method for cross-domain data fetching: JSON with Padding, or simply JSONP. - Bob Ippolito
緣由
為了安全,瀏覽器中的javascript不能存取位在不同domain的資料,這稱做same-origin policy。Same-Origin Policy Part 1: Why we’re stuck with things like XSS and XSRF/CSRF中說的很清楚:
Put simply, same-origin prevents a document or script loaded from one site of origin from manipulating properties of or communicating with a document loaded from another site of origin. In this case the term origin refers to the domain name, port, and protocol of the site hosting the document. The following list covers the major actions that cause the browser to check access against the same-origin policy:
- manipulating browser windows
- URLs requested via the XmlHttpRequest
- manipulating frames (including inline frames)
- manipulating documents (included using the object tag)
- manipulating cookie
但是HTML本身仍可以加入很多其他domain的資源。
The above restrictions don’t limit all interaction, however. There is no limitation on including documents from other sources in HTML tag elements. It’s fairly common for images, style sheets, and scripts to be included from other domains. In fact, the only time same-origin explicitly restricts document retrieval is when the XmlHttpRequest method is used.
若是需要cross domain取得資料時,例如使用其他網站提供的web service,就需要一些額外的輔助,例如local proxy,但是一般的使用者,無法在server上放proxy。
於是Bob Ippolito提出了以下的建議:
- 使用script tag載入javascript檔的方式,可以cross domain取得資料。
- server端傳回的資料採JSON(Javascript object notation)格式。
- server端提供一項服務,接受回呼函式(callback function)名稱為參數,將傳回的資料當成回呼函式的參數,傳回對此回呼函式的呼叫(padding 所指的就是此加上回呼函式的填充動作)。當此javascript傳輸完成,client端會自動執行,呼叫此回呼函式,因此在資料傳輸完畢時可以得知。
這裡需要的是server端的幫助,目前Google、Yahoo等的服務都有提供JSONP格式的回傳。
範例
以Flickr提供的服務查詢標籤包含羅平、油菜花、sunset的公開照片,並加入參數format=json、jsoncallback=doPhoto,表示傳回資料為JSON格式,並將其填入回呼函式doPhoto的呼叫中作為參數:
<script type="text/javascript" src="http://api.flickr.com/services/feeds/photos_public.gne?tags=羅平,油菜花,sunset&tagmode=all&format=json&jsoncallback=doPhoto">
會得到「回呼函式名稱(JSON格式資料)」這種格式的回傳:
doPhoto({
"title": "每個人的相片 已標籤 羅平, 油菜花 和 sunset",
"link": "http://www.flickr.com/photos/",
"description": "",
"modified": "2007-04-22T14:19:09Z",
"generator": "http://www.flickr.com/",
"items": [
{
"title": "羅平金雞嶺夕陽",
"link": "http://www.flickr.com/photos/38362352@N00/468370409/",
"media": {"m":"http://farm1.static.flickr.com/211/468370409_319f383a24_m.jpg"},
"date_taken": "2007-04-22T22:19:09-08:00",
"description": "...",
"published": "2007-04-22T14:19:09Z",
"author": "nobody@flickr.com (Nature RGB)",
"author_id": "38362352@N00",
"tags": "sunset yunnan 油菜花 羅平"
}
]
})
當回傳載入完畢後,client端執行上面這段javascript,便會呼叫之前定義好的doPhoto,這裡做的是秀出查詢的圖片(請原諒我憋腳的javascript):
function doPhoto(result) {
var div = document.getElementById("flickr photos");
for(var i=0; i < result.items.length; i++) {
var img = document.createElement("img");
img.setAttribute("src",result.items[i].media.m);
img.setAttribute("alt",result.items[i].title);
var a = document.createElement("a");
a.setAttribute("href",result.items[i].link);
a.setAttribute("target","_blank");
a.appendChild(img);
div.appendChild(a);
}
}