天天看點

AJAX 跨域通路 — 方法大全

Case I. Web代理的方式 (on Server A)   即使用者通路A網站時所産生的對B網站的跨域通路請求均送出到A網站的指定頁面,由該頁面代替使用者頁面完成互動,進而傳回合适的結果。此方案可以解決現階段所能夠想到的多數跨域通路問題,但要求A網站提供Web代理的支援,是以A網站與B網站之間必須是緊密協作的,且每次互動過程,A網站的伺服器負擔增加,且無法代使用者儲存session狀态。   Case II. on-Demand方式 (on Server A)   MYMSN的門戶就用的這種方式,不過 MYMSN中不涉及跨域通路問題。在頁面内動态生成新的<script>,将其src屬性指向别的網站的網址,這個網址傳回的内容必須是合法的Javascript腳本,常用的是JSON消息。此方案存在的缺陷是, script的src屬性完成該調用時采取的方式時get方式,如果請求時傳遞的字元串過大時,可能會無法正常運作。不過此方案非常适合聚合類門戶使用。   <html> <head> <script language="javascript" type="text/javascript"> function loadContent() { var s=document.createElement('SCRIPT'); s.src=' http://www.anotherdomain.com/TestCrossJS.aspx?f=setDivContent'; document.body.appendChild(s); } function setDivContent(v) { var dv = document.getElementById("dv"); dv.innerHTML = v; } </script> </head> <body> <div id="dv"></div> <input type="button" value="Click Me" οnclick="loadContent()"> </body> </html> 其中的www.anotherdomain.com/TestCrossJS.aspx是這樣的, <script language="C#" runat="server"> void Page_Load(object sender, EventArgs e) {   string f = Request.QueryString["f"];   Response.Clear();   Response.ContentType = "application/x-javascript";   Response.Write(String.Format(@"                    {0}('{1}');",                     f,                    DateTime.Now));   Response.End(); } </script> 點選“Click Me”按鈕,生成一個新的script tag,下載下傳對應的 Javascript 腳本,結束時回調其中的setDivContent(),進而更新網頁上一個div的内容。     Case III. iframe方式 (on Server A)   檢視過醒來在javaeye上的一篇關于跨域通路的文章,他提到自己已經用iframe的方式解決了跨域通路問題。資料送出跟擷取,采用iframe這種方式的确可以了,但由于父視窗與子視窗之間不能互動(跨域通路的情況下,這種互動被拒絕),是以無法完成對父視窗效果的影響。  在頁面内嵌或動态生成指向别的網站的IFRAME,然後這2個網頁間可以通過改變對方的anchor hash fragment來傳輸消息。改變一個網頁的anchor hash fragment并不會使浏覽器重新裝載網頁,是以一個網頁的狀态得以保持,而網頁本身則可以通過一個計時器(timer)來察覺自己anchor hash的變化,進而相應改變自己的狀态。   1. http://domain1/TestCross.html: <html> <head> <script language="javascript" type="text/javascript"> var url = " http://domain2/TestCross.html" var oldHash = null; var timer = null; function getHash() { var hash = window.location.hash; if ((hash.length >= 1) && (hash.charAt(0) == '#')) { hash = hash.substring(1); } return hash; } function sendRequest() { var d = document; var t = d.getElementById('request'); var f = d.getElementById('alienFrame'); f.src = url + "#" + t.value + "<br/>" + new Date(); } function setDivHtml(v) { var d = document; var dv = d.getElementById('response'); dv.innerHTML = v; } function idle() { var newHash = getHash(); if (newHash != oldHash) { setDivHtml(newHash); oldHash = newHash; } timer = window.setTimeout(idle, 100); } function window.onload() { timer = window.setTimeout(idle, 100); } </script> </head> <body> 請求:<input type="text" id="request"> <input type="button" value="發送" οnclick="sendRequest()" /><br/> 回複:<div id="response"></div> <iframe id="alienFrame" src=" http://domain2/TestCross.html"></iframe> </body> </html> 2. http://domain2/TestCross.html: <html> <head> <script language="javascript" type="text/javascript"> var url = " http://domain1/TestCross.html" var oldHash = null; var timer = null; function getHash() { var hash = window.location.hash; if ((hash.length >= 1) && (hash.charAt(0) == '#')) { hash = hash.substring(1); } return hash; } function sendRequest() { var d = document; var t = d.getElementById('request'); var f = parent; //alert(f.document); //試着去掉這個注釋,你會得到“Access is denied” f.location.href = url + "#" + t.value + "<br/>" + new Date(); } function setDivHtml(v) { var d = document; var dv = d.getElementById('response'); dv.innerHTML = v; } function idle() { var newHash = getHash(); if (newHash != oldHash) { setDivHtml(newHash); oldHash = newHash; } timer = window.setTimeout(idle, 100); } function window.onload() { timer = window.setTimeout(idle, 100); } </script> </head> <body> 請求:<input type="text" id="request"> <input type="button" value="發送" οnclick="sendRequest()" /><br/> 回複:<div id="response"></div> </body> </html> 兩個網頁基本相同,第一個網頁内嵌一個IFRAME,在點選“發送”按鈕後,會将文本框裡的内容通過hash fragment傳給IFRAME。點選IFRAME裡的“發送”按鈕後,它會将文本框裡的内容通過hash fragment傳給父視窗。因為是隻改動了hash fragment,浏覽器不會重新load網頁内容,這裡使用了一個計時器來檢測URL變化,如果變化了,就更新其中一個div的内容 。    Case IV. 使用者本地轉儲方式 (local)   IE本身依附于windows平台的特性為我們提供了一種基于iframe,利用記憶體來“繞行”的方案,即兩個window之間可以在用戶端通過windows剪貼闆的方式進行資料傳輸,隻需要在接受資料的一方設定Interval進行輪詢,獲得結果後清除Interval即可。FF的平台獨立性決定了它不支援剪貼闆這種方式,而以往版本的FF中存在的插件漏洞又被fixed了,是以FF無法通過記憶體來完成暗渡陳倉。而由于檔案操作FF 也沒有提供支援(無法通過Cookie跨域完成資料傳遞),緻使這種技巧性的方式隻能在IE中使用。     Case V: (其實還是在服務端A用iframe解決了與伺服器B通信的問題)   要解決的問題:發生在使用者送出網頁 URL (還包括 Tag, Notes 等)給 Bookmark 伺服器時。 關于 URL 的送出至少可以有三種方式:   1.       登陸 Bookmark 伺服器的送出頁面,将要收藏的 URL 通過該頁面送出給伺服器。 2.       安裝浏覽器插件,通過插件将 URL 送出給伺服器。 3.       從 Bookmark 伺服器動态加載 javascript 小工具到目前頁面,通過它來完成送出工作。        第一種方式開發起來最簡單,但對使用者來講比較麻煩,每次都需要先登陸 Bookmark 伺服器才能完成送出;第二種方式我并不熟悉插件開發,而且使用者也不喜歡太多的插件堆滿自己的浏覽器;第三種方式開發難度小,又避免了每次登陸伺服器的麻煩,是以最終采用它。第三種方式中動态加載的 javascript 小工具除了需要生成 UI 供使用者填寫資訊( URL , tag , notes 等),當使用者點選送出的時候,還要完成與伺服器通信的功能。        跨域通路,簡單來說就是 A 網站的 javascript 代碼試圖通路 B 網站,包括送出内容和擷取内容。由于安全原因,跨域通路是被各大浏覽器所預設禁止的。寫過跨域通路 ajax 的朋友相信都遇到過被告知“沒有權限”的情況。通過 XMLHttp 來發送資料給 Bookmark 伺服器的嘗試失敗了。于是,看到網上的一些資料,我又開始嘗試用 javascript 小工具在使用者網頁動态建立一個隐藏的 iframe, iframe 的 src 指向伺服器的一個 servlet ,試圖通過調用 iframe 中提供的 javascript 來完成與伺服器的通信。但不幸的是,使用者網頁中的 javascript 代碼通路 iframe 也被浏覽器歸為跨域通路(特指 iframe 的 src 指向其它網站的情形),嘗試再次失敗。   最終,在一篇文章中看到,與 iframe 不同,如果 A 網站從 B 網站加載 javascript , A 網站可以自由的通路該 javascript 的内容,并不會被浏覽器認為是跨域通路。模仿剛才 iframe 的思路,當使用者點選送出時,可以動态建立一個 javascript 對象,該對象的 src 指向 Bookmark 伺服器的一個 servlet ,注意: URL 、 Tag 、 Notes 、 User 、Password 等資訊被作為 src URL 參數傳給伺服器。請看下面的代碼:  

  var url = "http://localhost:8080/Deeryard/BookmarkServlet?" +            "url=" + url_source + "&" + "title=" + title + "&" + "tag=" + tag + "&" + "notes=" + notes + "&" + "user=" + user + "&" + "password=" + password;   url = encodeURI(url);     //Submit to server with a trick var js_obj = document.createElement( "script" ); js_obj.type = "text/javascript" ; js_obj.setAttribute( "src" , url);     //Get response from server by appending it to document document.body.appendChild(js_obj);

    上面例子中, js_obj.setArrribute() 将資訊作為 src 的 URL 參數送出給了 Bookmark servlet 。那麼使用者又如何取得伺服器的響應資訊呢?答案就是最末一行代碼, servlet 的輸出必須是 javascript 代碼,它可以調用使用者網頁上的其他 javascript 函數,以及操作 dom 對象。下面的 servlet 代碼生成了一個 javascript 函數調用:   out.write("onServerResponse(INADEQUATE_INFORMATION);");   document.body.appendChild(js_obj) 執行後 onServerResponse( INADEQUATE_INFORMATION) 就會得到執行,使客戶網頁響應伺服器結果。這樣一個完整的通信過程就完成了。   CaseVI:Tomcat + PHP + HTML(含JS)(on Server A)   伺服器A上已經裝好了Tomcat, 我們寫一個test.html(含JS),再寫一個PHP檔案(由其來完成跨域通信要求)。   更多地,請參考: https://www6.software.ibm.com/developerworks/cn/education/xml/x-ajaxtrans/index.html http://www.xyhhxx.com/news/net/20061013121041.htm

繼續閱讀