環境描述
- 服務器:使用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的跨域訪問這篇文章替我找到了回家的路!