Ajax跨域訪問問題-方法大全

來源:互聯網
上載者:User

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>
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></div>

<input value="Click Me">
</body>
</html>

其中的www.anotherdomain.com/TestCrossJS.aspx是這樣的,

<script 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>
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> <input value="發送" /><br/>
回複:<div></div>

<iframe src="http://domain2/TestCross.html"></iframe>

</body>
</html>

2. http://domain2/TestCross.html:

<html>
<head>
<script>
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> <input value="發送" /><br/>
回複:<div></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檔案(由其來完成跨域通訊要求)。

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>
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></div>

<input value="Click Me">
</body>
</html>

其中的www.anotherdomain.com/TestCrossJS.aspx是這樣的,

<script 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>
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> <input value="發送" /><br/>
回複:<div></div>

<iframe src="http://domain2/TestCross.html"></iframe>

</body>
</html>

2. http://domain2/TestCross.html:

<html>
<head>
<script>
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> <input value="發送" /><br/>
回複:<div></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檔案(由其來完成跨域通訊要求)。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.