當我們開發一個WEB應用時,不可避免地使用一些Ajax來實現非同步資料載入,以構建富用戶端。
不過使用Ajax有一個問題:同源策略可能會使我們無法取得所需資料。
(註:同源策略指Ajax的請求方和資料返回者必須在同一IP和連接埠下,例如我們的網頁地址在127.0.0.1:8080下,那麼它發起的Ajax請求就只能被此IP和連接埠的監聽程式接收和處理。有網友指出即時同一台主機的多個IP也不可以互發Ajax請求,這個我沒有求證。)
由於Ajax的同源策略,造成我們在以下情況下可能無法取得所需資料(就Ajax而言):
1.需要在頁面上使用指令碼(JavaScript)去取得其他伺服器的資料(不同於WebService,我們一般在後台使用Http請求來取得資料然後返回給瀏覽器)。
2.需要將網頁儲存到本地(即離線使用),在瀏覽器載入網頁時使用指令碼從伺服器擷取資料。
特別是第二種,在現今行動裝置 App開發非常適用。由於各個行動裝置平台都提供了很好支援,使用Html5和Css3標準的網頁甚至可以媲美本地應用,而且其跨平台特性比至於本地應用更具優勢!
那麼說了Ajax在“跨域”上的局限性之後我們不得不使用一些技巧來實現技能非同步載入和跨域。
JsonP映入我們的眼帘,JsonP是一種技巧或使用模式。它也使用了Json格式傳輸資料,但這並非必要(Ajax也如此)。
我們使用Ajax不能跨域時又發現:
1.普通的Http請求可以跨域。
2.<script src=""></script>、<link ref=""/>、<img src=""/>這些標籤資料載入時使用的是Http請求,而且不是同步的(此處筆者沒有過多研究,如果有興趣,讀者可以試試閱讀WebKit瀏覽器核心源碼)!!!
那麼我們是否可以使用指令碼標籤來實現跨域資料請求呢?答案是肯定的!
我們知道:只要是資料請求,一定是有一定規則的。實質上網頁、圖片、css和js都是使用Http請求(一般為Get)來實現的,伺服器程式會返回對應資料(如js指令碼內容、css樣式表內容、圖片內容等),只不過我們平時在進行伺服器程式邏輯設計時將資源檔請求忽略了,讓伺服器程式來預設處理。
至此,JsonP跨域原理就已經很清晰了:使用<script></script>標籤來產生Http(Get)請求來從伺服器擷取資料(平時我們是請求具體的資源檔如jquery.js,而伺服器程式會預設把對應檔案以字串形式返回給瀏覽器來處理;而現在我們需要請求具體的控制器,讓伺服器程式返回的字串裡包含我們所需要的資料)。
為了便於讀者理解,我繪製了一副草圖:
從不難看出:我們拜託了JavaScript幫我們帶回資料。
以下是具體代碼:讀者如果不是很理解的話可以從CallBack作為突破口,理解了它,JsonP基本就很清晰地呈現在你面前了。
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 2 <html> 3 <head> 4 <title> New Document </title> 5 <meta name="Generator" content="EditPlus"> 6 <meta name="Author" content=""> 7 <meta name="Keywords" content=""> 8 <meta name="Description" content=""> 9 <script type="text/javascript">10 function loadJsonP(){11 var url = "http://localhost:8080/test/jsonp.do?callback=CallBack";12 var jsonpScript = document.createElement('script'); // 建立為我們服務的指令碼標籤13 jsonpScript.setAttribute('src', url); // 設定src14 document.getElementsByTagName('head')[0].appendChild(jsonpScript); // 將標籤添加到頁面,瀏覽器會以標籤src屬性為url擷取指令碼內容15 }16 // 指令碼內容即為對下面函數的調用,同時傳回了資料17 function CallBack(data){18 // 對資料的處理...19 var showarea = document.getElementById('ta');20 showarea.innerHTML = "資料取回!id:" + data.id + "name:" + data.name;21 }22 </script>23 </head>24 25 <body>26 <textarea id="ta"></textarea>27 <a href="javascript:loadJsonP()">jsonP</a>28 </body>29 </html>
(上面的代碼為本地html網頁,即並非通過瀏覽器地址欄輸入來訪問伺服器網頁)
1 public void doGet(HttpServletRequest request, HttpServletResponse response) 2 throws ServletException, IOException { 3 response.setCharacterEncoding("gbk"); 4 response.setContentType("text/javascript"); 5 PrintWriter out = response.getWriter(); 6 String json = "{\"id\": \"100\",\"name\": \"張三\"}"; 7 String callback = request.getParameter("callback"); 8 /*try { 9 Thread.sleep(3000);10 } catch (InterruptedException e) {11 e.printStackTrace();12 }*/13 out.write(callback+"("+json+")");14 out.flush();15 out.close();16 }
(上面的代碼可以用於返回指令碼字串,同時帶回資料。只要用戶端和服務端定義好回呼函數名即可)
歡迎您移步我們的交流群,無聊的時候大家一起打發時間:
或者通過QQ與我聯絡:
(最後編輯時間2013-06-13 22:44:57)