AJAX跨域擷取資料

來源:互聯網
上載者:User

ajax可以在不重新整理頁面的情況下,擷取伺服器端資料。不過由於各瀏覽器的安全限制,ajax很可能不允許跨域訪問(這類限制是必要的,如果不加任何限制,ajax可以通過"file://xxx"擷取本地主機的一些重要訊息並發送往伺服器,造成安全隱患)。
但有的時候,位於a域(假設網域名稱為a.com)的頁面確實需要訪問b域(假設網域名稱為b.com)並擷取一些資料,有沒有辦法實現這樣的需求呢?
傳統的方式是在a域的伺服器端處理,即用戶端訪問a.com的一個頁面,a.com在伺服器端通過http協議訪問b域,擷取資料後返回給用戶端。相當於a.com伺服器充當了代理。
這種方式的缺點是,每次訪問a.com的這個頁面,都會造成整頁重新整理。下面的情境不太好處理:b.com對請求的處理比較耗時,它允許客戶(這裡的客戶包括a.com和瀏覽器用戶端等)的請求立即返回,但後續需要不斷輪詢以查詢處理進度。由於B-S架構不支援伺服器端主動向用戶端發訊息,使用ajax輪詢來類比這種行為是很常見的。如果採用剛才提到的方法(a.com伺服器做代理),每次輪詢都造成整頁重新整理,使用者體驗不好。最好的解決方案還是在ajax中直接存取b.com。

ajax不能跨域訪問,也就是說,a.com伺服器產生的頁面,不能通過ajax直接存取b.com的資源。有一種巧妙的辦法繞過這種安全機制:

我們注意到,HTML的script標籤,src屬性可以跨域制定js檔案的來源,即下面的代碼是合法的

<script src="http://b.com/xxx.js"></script>

這段代碼即使放到a.com產生的頁面中,也能正確的載入到b.com中的js檔案,並執行裡面的代碼。
如果a.com和b.com的開發人員可以相互交流,或是b.com明確支援下面提到的約定,ajax可以通過這樣的機制,擷取b.com上的資料(這些限制使得下面的方式不被視為一種嚴重的安全性漏洞)。具體做法是:

  1. a.com的頁面動態產生一個script元素,src屬性設定為b.com上擷取資料的url,比如是http://b.com/xxx?xxx,同時,在這個資源url最後加上一個特殊的參數,變成http://b.com/xxx?xxx&callback=fun。注意,這裡的callback是個約定名稱(在b.com的伺服器端要特別處理callback名字的參數)。也就是說,a.com的頁面中可以將這個參數名改成mycallback等任意的名字,但必須告訴b.com這個名字到底是什麼。callback的值"fun"是一個a.com頁面上實現的函數的函數名。
  2. script允許跨域訪問,a.com會向b.com申請這個url對應的js檔案(代碼)。b.com拿到這個url,取出callback參數,獲得"fun"這個字串。
  3. b.com動態產生js檔案(代碼),其中插入一段代碼"fun(...)"。參數通常是一個JSON對象(這是慣例,a.com和b.com也可以協定別的參數類型),比如"fun({p1:1,p2:2})"。b.com將包含這句代碼的js檔案(代碼)發送給a.com。
  4. a.com的script標籤擷取到來自b.com的js檔案(代碼)後,立即執行,這時,會執行到"fun({p1:1,p2:2})"這句代碼。注意到fun是a.com頁面中的一個函數,則這裡是個函數調用。此時a.com的fun函數就可以接收到b.com傳過來的JSON對象({p1:1,p2:2})了。

JQuery也是使用類似的機制實現的JSONP,一個例子如下,用戶端

$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?",    function(data){      $.each(data, function(i,item){        $("p").append(i+": "+item+"<br>");      });    });

注意這裡調用的url中包含"jsoncallback=?","?"會被JQuery替換成實際的回呼函數名,使用者(網頁編寫者)不用關心。跨網域服務器響應時,JQuery保證能調用到使用者編寫的匿名函數(例子中$.getJSON()的第二個參數)。
伺服器端如果是JSP,代碼大致如下

...String jsoncallback=request.getParameter("jsoncallback");...PrintWriter out = response.getWriter();out.print(jsoncallback+"({\"account\":\"XX\",\"passed\":\"true\",\"error\":\"null\"})");

Jquery取得的資料可能如下

JQUET0988788({"account":"XX","passed":"true","error":"null"})

JQUET0988788調用會調用到使用者自己編寫的匿名函數。

[小貼士]

IE核心的瀏覽器在使用script.src方式請求資源時,request不會攜帶sessionid資訊,對於伺服器端而言,每次請求都來自不同的用戶端,則在ajax輪詢的情境裡,伺服器端可能無法準確判斷用戶端的身份而導致需求難以實現。FireFox、Chrome等瀏覽器沒有這樣的問題(它們的請求可以攜帶sessionid)。

解決辦法是:用戶端在script.src的請求url中加入特殊的參數以攜帶sessionid(類似上面的callback參數,與b.com做好約定),如果用戶端還未擷取sessionid,這個參數可以不指定;伺服器擷取這個sessionid,通過它來判斷用戶端的身份,如果url中沒有指定,則使用當前自動產生的sessionid(request.getSession().getId()),並把它加入返回的json對象的某個欄位中;用戶端擷取這個欄位的值,並把它加入下次請求的url中。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.