標籤:des http os io 使用 java ar strong for
http://www.ibm.com/developerworks/cn/web/wa-aj-jsonp1/
簡介
Asynchronous JavaScript and XML (Ajax) 是驅動新一代 Web 網站(流行術語為 Web 2.0 網站)的關鍵技術。Ajax 允許在不干擾 Web 應用程式的顯示和行為的情況下在後台進行資料檢索。使用 XMLHttpRequest
函數擷取資料,它是一種 API,允許用戶端 JavaScript 通過 HTTP 串連到遠程伺服器。Ajax 也是許多 mashup 的驅動力,它可將來自多個地方的內容整合為單一 Web 應用程式。
不過,由於受到瀏覽器的限制,該方法不允許跨域通訊。如果嘗試從不同的域請求資料,會出現安全錯誤。如果能控制資料駐留的遠程伺服器並且每個請求都前往同一域,就可以避免這些安全錯誤。但是,如果僅停留在自己的伺服器上,Web 應用程式還有什麼用處呢?如果需要從多個第三方伺服器收集資料時,又該怎麼辦?
理解同源策略限制
同源策略阻止從一個域上載入的指令碼擷取或操作另一個域上的文件屬性。也就是說,受到請求的 URL 的域必須與當前 Web 頁面的域相同。這意味著瀏覽器隔離來自不同源的內容,以防止它們之間的操作。這個瀏覽器策略很舊,從 Netscape Navigator 2.0 版本開始就存在。
克服該限制的一個相對簡單的方法是讓 Web 頁面向它源自的 Web 服務器請求資料,並且讓 Web 服務器像代理一樣將請求轉寄給真正的第三方伺服器。儘管該技術獲得了普遍使用,但它是不可伸縮的。另一種方式是使用架構要素在當前 Web 頁面中建立新地區,並且使用 GET
請求擷取任何第三方資源。不過,擷取資源後,架構中的內容會受到同源策略的限制。
克服該限制更理想方法是在 Web 頁面中插入動態指令碼元素,該頁面源指向其他域中的服務 URL 並且在自身指令碼中擷取資料。指令碼載入時它開始執行。該方法是可行的,因為同源策略不阻止動態指令碼插入,並且將指令碼看作是從提供 Web 頁面的域上載入的。但如果該指令碼嘗試從另一個域上載入文檔,就不會成功。幸運的是,通過添加 JavaScript Object Notation (JSON) 可以改進該技術。
JSON 和 JSONP
JSON 是用於在瀏覽器和伺服器之間交換資訊的輕量級資料格式(與 XML 相比)。JOSON 依賴於 JavaScript 開發人員,因為它是 JavaScript 對象的字串表示。例如,假設有一個含兩個屬性的 ticker 對象:symbol 和 price。這是在 JavaScript 中定義 ticker 對象的方式:
var ticker = {symbol: ‘IBM‘, price: 91.42};
並且這是它的 JSON 表示方式:
{symbol: ‘IBM‘, price: 91.42}
從 參考資料 尋找更多有關 JSON 和將其作為資料內部交換格式的資訊。清單 1 定義了一個 JavaScript 函數,調用該函數時會顯示 IBM 的股價。(我們沒有詳細介紹如何將該函數添加到 Web 頁面)。
清單 1. 定義 showPrice 函數
function showPrice(data) { alert("Symbol: " + data.symbol + ", Price: " + data.price);}
可以將 JSON 資料作為參數傳遞,以調用該函數:
showPrice({symbol: ‘IBM‘, price: 91.42}); // alerts: Symbol: IBM, Price: 91.42
現在準備將這兩個步驟包含到 Web 頁面,如清單 2 所示。
清單 2. 在 Web 頁面中包含 showPrice 函數和參數
<script type="text/javascript">function showPrice(data) { alert("Symbol: " + data.symbol + ", Price: " + data.price);}</script><script type="text/javascript">showPrice({symbol: ‘IBM‘, price: 91.42});</script>
載入頁面後,應該看 1 所示的警告。
圖 1. IBM ticker
至此,本文已展示了如何將靜態 JSON 資料作為參數調用 JavaScript 函數。不過,通過在函數調用中動態封裝 JSON 資料可以用動態資料調用函數,這是一種動態 JavaScript 插入的技術。要查看其效果,將下面一行放入名為 ticker.js 的獨立 JavaScript 檔案中。
showPrice({symbol: ‘IBM‘, price: 91.42});
現在改變 Web 頁面中的指令碼,使其和清單 3 一樣。
清單 3. 動態 JavaScript 插入代碼
<script type="text/javascript">// This is our function to be called with JSON datafunction showPrice(data) { alert("Symbol: " + data.symbol + ", Price: " + data.price);}var url = “ticker.js”; // URL of the external script// this shows dynamic script insertionvar script = document.createElement(‘script‘);script.setAttribute(‘src‘, url);// load the scriptdocument.getElementsByTagName(‘head‘)[0].appendChild(script); </script>
在清單 3 所示的例子中,動態插入的 JavaScript 代碼位於 ticker.js 檔案中,它將真正的 JSON 資料作為參數調用 showPrice()
函數。
前面已經提到,同源策略不阻止將動態指令碼元素插入文檔中。也就是說,可以動態插入來自不同域的 JavaScript,並且這些域都攜帶 JSON 資料。這其實是真正的 JSONP(JSON with Padding):打包在函數調用中的 JSON 資料。注意,為了完成該操作,Web 頁面必須在插入時具有已經定義好的回呼函數,也就是我們例子中的 showPrice()
。
不過,所謂的 JSONP 服務(或 Remote JSON Service)是一種帶有附加功能的 Web 服務,該功能支援在特定於使用者的函數調用中打包返回的 JSON 資料。這種方法依賴於接受回呼函數名作為請求參數的遠程服務。然後該服務產生對該函數的調用,將 JSON 資料作為參數傳遞,在到達用戶端時將其插入 Web 頁面並開始執行。
回頁首
jQuery 的 JSONP 支援
從 1.2 版本開始,jQuery 擁有對 JSONP 回調的本地支援。如果指定了 JSONP 回調,就可以載入位於另一個域的 JSON 資料,回調的文法為:url?callback=?
。
jQuery 自動將 ? 替換為要調用的產生函數名。清單 4 顯示了該代碼。
清單 4. 使用 JSONP 回調
jQuery.getJSON(url+"&callback=?", function(data) { alert("Symbol: " + data.symbol + ", Price: " + data.price);});
為此,jQuery 將一個全域函數附加到插入指令碼時需要調用的視窗對象。另外,jQuery 也能最佳化非跨域調用。如果向同一個域發出請求,jQuery 就將其轉化為普通 Ajax 請求。
使用 JSONP 支援的樣本服務
在上一個例子中,使用了靜態檔案(ticker.js)將 JavaScript 動態插入到 Web 頁面中。儘管返回了 JSONP 回複,但它不允許您在 URL 中定義回呼函數名。這不是 JSONP 服務。因此,如何才能將其轉換為真正的 JSONP 服務呢?可使用的方法很多。這裡我們將分別使用 PHP 和 Java 展示兩個樣本。
首先,假設您的服務在所請求的 URL 中接受了一個名為 callback
的參數。(參數名不重要,但是客戶和伺服器必須都同意該名稱)。另外假設向服務發送的請求是這樣的:
http://www.yourdomain.com/jsonp/ticker?symbol=IBM&callback=showPrice
在這種情況下,symbol
是表示請求 ticker symbol 的請求參數,而 callback
是 Web 應用程式的回呼函數的名稱。使用清單 5 所示的代碼可以通過 jQuery 的 JSONP 支援調用該服務。
清單 5. 調用回調服務
jQuery.getJSON("http://www.yourdomain.com/jsonp/ticker?symbol=IBM&callback=?", function(data) { alert("Symbol: " + data.symbol + ", Price: " + data.price);});
注意,我們使用 ?
作為回呼函數名,而非真實的函數名。因為 jQuery 會用產生的函數名替換 ?
。所以您不用定義類似於 showPrice()
的函數。
清單 6 顯示了用 PHP 實現的 JSONP 服務的一段代碼。
清單 6. 用 PHP 實現的 JSONP 服務的程式碼片段
$jsonData = getDataAsJson($_GET[‘symbol‘]);echo $_GET[‘callback‘] . ‘(‘ . $jsonData . ‘);‘;// prints: jsonp1232617941775({"symbol" : "IBM", "price" : "91.42"});
清單 7 顯示了具有同樣功能的 Java™ Servlet 方法。
清單 7. 用 Java servlet 實現的 JSONP 服務
@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String jsonData = getDataAsJson(req.getParameter("symbol"));String output = req.getParameter("callback") + "(" + jsonData + ");";resp.setContentType("text/javascript"); PrintWriter out = resp.getWriter();out.println(output);// prints: jsonp1232617941775({"symbol" : "IBM", "price" : "91.42"});}
那麼,如果要構建 mashup 應該怎麼辦,是從第三方伺服器收集內容,並在單一的 Web 頁面中顯示它們嗎?答案很簡單:您必須使用第三方 JSONP 服務。這種服務並不少。
現成的 JSONP 服務
知道如何使用 JSONP 之後,可以開始使用一些現成的 JSONP Web 服務來構建應用程式和 mashup。下面為接下來的開發項目做準備。(提示:您可以複製特定的 URL 並將其粘貼到瀏覽器的地址欄,以檢查產生的 JSONP 響應)。
Digg API:來自 Digg 的頭條新聞:
http://services.digg.com/stories/top?appkey=http%3A%2F%2Fmashup.com&type=javascript&callback=?
Geonames API:郵編的位置資訊:
http://www.geonames.org/postalCodeLookupJSON?postalcode=10504&country=US&callback=?
Flickr API:來自 Flickr 的最新貓圖片:
http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?
Yahoo Local Search API:在郵編為 10504 的地區搜尋比薩:
http://local.yahooapis.com/LocalSearchService/V3/localSearch?appid=YahooDemo&query=pizza&zip=10504&results=2&output=json&callback=?
回頁首
重要提示
JSONP 是構建 mashup 的強大技術,但不幸的是,它並不是所有跨域通訊需求的萬靈藥。它有一些缺陷,在提交開發資源之前必須認真考慮它們。第一,也是最重要的一點,沒有關於 JSONP 調用的錯誤處理。如果動態指令碼插入有效,就執行調用;如果無效,就靜默失敗。失敗是沒有任何提示的。例如,不能從伺服器捕捉到 404 錯誤,也不能取消或重新開始請求。不過,等待一段時間還沒有響應的話,就不用理它了。(未來的 jQuery 版本可能有終止 JSONP 請求的特性)。
JSONP 的另一個主要缺陷是被不信任的服務使用時會很危險。因為 JSONP 服務返回打包在函數調用中的 JSON 響應,而函數調用是由瀏覽器執行的,這使宿主 Web 應用程式更容易受到各類攻擊。如果打算使用 JSONP 服務,瞭解它能造成的威脅非常重要。(參見 參考資料 瞭解更多資訊)。
回頁首
結束語
在該系列的第一篇文章中,我們講解了如何結合使用 JSONP 和 jQuery 快速構建強大的 mashup。主要主題包括:
- 瀏覽器同源策略的限制以及解決辦法
- 作為一種有效跨域通訊技術,JSONP 能夠繞過當前瀏覽器的同源策略限制
- JSONP 使 Web 應用程式開發人員能夠快速構建 mashup
- 樣本 JSONP 服務及其使用:Ticker 服務
本系列的下一篇文章將介紹 Yahoo! 查詢語言(YQL),這種單端點 JSONP 服務允許您跨 Web 查詢、過濾和合并資料。最後還使用 YQL 和 jQuery 構建 mashup 應用程式。
使用 JSONP 實現跨域通訊,第 1 部分: 結合 JSONP 和 jQuery 快速構建強大的 mashup