環境描述
- 伺服器:使用jersey提供RESTful接口
- 用戶端:使用jquery的getJson()方法異步擷取資料
- 希望實作的功能:伺服器端以json格式提供RESTful的資料通路,用戶端通過ajax異步加載的方式讀取資料。
出問題代碼
(其實下面代碼也不包含錯誤,隻是在執行的時候會出問題)
用戶端原始代碼:
document.getElementById("mydiv").innerHTML = "data";
$(function(){
$.getJSON("******", function (data) {
alert("go");
document.getElementById("mydiv").innerHTML = "hahahah";
});
});
伺服器端原始代碼:
@GET
@Path("/test")
@Produces("application/json;charset=utf8")
public String go(){
return "({\"num\":\"3\"}";
}
結果運作的時候就出問題了,用戶端的回調函數死活不執行。用Firefox的Httpfox調試下,發現在擷取json資料的時候就遇到問題,報錯
application/json (NS_ERROR_DOM_BAD_URI)
。
網上找了下,說是jquery跨域通路的安全問題,就是說:
JavaScript出于安全方面的考慮,不允許跨域調用其他頁面的對象。但在安全限制的同時也給注入iframe或是ajax應用上帶來了不少麻煩。首先什麼是跨域,簡單地了解就是因為JavaScript同源政策的限制,a.com 域名下的js無法操作b.com或是c.a.com域名下的對象。
上邊的代碼中,用戶端和服務端确實在不同的伺服器,使用不同的IP位址和端口号,是以出現了問題。
解決辦法就是JSONP,全稱是 JSON with Padding ,是基于 JSON 格式的為解決跨域請求資源而産生的解決方案。其基本原理是利用了 HTML 裡 元素标簽可以跨域通路,是以将資料封裝成為js回調函數的格式進行傳輸通路。
舉個例子,上述的資料
{"num":"3"}
,這時不能跨域通路的,那麼救把它包裝為js,
callback('{"num":"3"}')
,将資料包裝為回調函數的參數,就可以跨域通路了。這就需要解決一個問題,提供資料通路的伺服器必須進行資料封裝的操作,如果響應的時候還是把原來的json格式資料輸出,那麼用戶端是拿不到需要的資料的。
如果說伺服器沒有對資料進行封裝,那麼出現的問題就是回調函數死活不執行。即使用Httpfox進行調試,也會發現雖然正确通路了資料連結,伺服器也傳回了資料,但回調函數就是執行不了。就是因為這個問題困了我整整兩天……
解決方案
其實jQuery已經封裝了JSONP的使用
$.getJSON( "****?jsoncallback=?", function( data ){
// 處理跨域請求得到的資料
});
jquery會把
jsoncallback
後邊的
?
替換為一個字元串作為資料通路的回調函數的函數名,在伺服器端需要讀取該參數,然後使用它對資料進行封裝,也就是改成
f(data)
的樣式,加上函數名和括号就行了。
伺服器端代碼為:
@GET
@Path("/test")
@Produces("application/json;charset=utf8")
public String go(@QueryParam("jsoncallback") String jsoncallback){
System.out.println(jsoncallback);
String info = jsoncallback+"({\"num\":\"3\"})";
return info;
}
ok,再通路就沒有問題了!
PS:最後吐槽一下網絡上的各種解決方案,都是隻給一個
jsoncallback
參數,伺服器需要怎麼改一點都不說,結果整整兩天一直困在回調函數不執行的苦惱中……
感謝基于JQuery、Jsonp與Jersey的跨域通路這篇文章替我找到了回家的路!