Javascript跨域和Ajax跨域解決方案總結

來源:互聯網
上載者:User

最近做的一個項目中需要ajax跨域取得資料,如果是在本域中確實沒有問題,但是放到二級域和其他域下瀏覽器直接就彈出提示框:“該頁正在訪問其控制範圍之外的資料,這有些危險,是否繼續"

1.什麼引起了ajax跨域不能的問題
ajax本身實際上是通過XMLHttpRequest對象來進行資料的互動,而瀏覽器出於安全考慮,不允許js代碼進行跨網域作業,所以會警告。

2.有什麼完美的解決方案嗎?
沒有。解決方案有不少,但是只能是根據自己的實際情況來選擇。

具體情況有:
一、本域和子域的相互訪問: www.aa.com和book.aa.com
二、本域和其他域的相互訪問: www.aa.com和www.bb.com 用 iframe
三、本域和其他域的相互訪問: www.aa.com和www.bb.com 用 XMLHttpRequest訪問代理
四、本域和其他域的相互訪問: www.aa.com和www.bb.com 用 JS建立動態指令碼

解決方法
一、如果想做到資料的互動,那麼www.aa.com和book.aa.com必須由你來開發才可以。可以將book.aa.com用iframe添加到
www.aa.com的某個頁面下,在www.aa.com和iframe裡面都加上document.domain =
"aa.com",這樣就可以統一域了,可以實現跨域訪問。就和平時同一個域中鑲嵌iframe一樣,直接調用裡面的JS就可以了。(這個辦法我沒有嘗
試,不過理論可行)

二、當兩個域不同時,如果想相互調用,那麼同樣需要兩個域都是由你來開發才可以。用iframe可以實現資料的互相調用。解決方案就是用
window.location對象的hash屬性。hash屬性就是http://domian/web/a.htm#dshakjdhsjka
裡面的#dshakjdhsjka。利用JS改變hash值網頁不會重新整理,可以這樣實現通過JS訪問hash值來做到通訊。不過除了IE之外其他大部分瀏
覽器只要改變hash就會記錄曆史,你在前進和後退時就需要處理,非常麻煩。不過再做簡單的處理時還是可以用的,具體的代碼我再下面有下載。大體的過程是
頁面a和頁面b在不同域下,b通過iframe添加到a裡,a通過JS修改iframe的hash值,b裡面做一個監聽(因為JS只能修改hash,資料
是否改變只能由b自己來判斷),檢測到b的hash值被修改了,得到修改的值,經過處理返回a需要的值,再來修改a的hash值(這個地方要注意,如果a
本身是那種查詢頁面的話比如http://domian/web/a.aspx?id=3,在b中直接parent.window.location是無
法取得資料的,同樣報沒有許可權的錯誤,需要a把這個傳過來,所以也比較麻煩),同樣a裡面也要做監聽,如果hash變化的話就取得返回的資料,再做相應的
處理。

三、這種情形是最經常遇到的,也是用的最多的了。就是www.aa.com和www.bb.com你只能修改一個,也就是另外一個是別人的,人家告訴你你
要取得資料就訪問某某串連參數是什麼樣子的,最後返回資料是什麼格式的。而你需要做的就是在你的域下建立一個網頁,讓伺服器去別人的網站上取得資料,再返
回給你。domain1下的a向同域下的GetData.aspx請求資料,GetData.aspx向domain2下的
ResponseData.aspx發送請求,ResponseData.aspx返回資料給GetData.aspx,
GetData.aspx再返回給a,這樣就完成了一次資料請求。GetData.aspx在其中充當了代理的作用。具體可以看下My Code。

四、這個和上個的區別就是請求是使用<script>標籤來請求的,這個要求也是兩個域都是由你來開發才行。原理就是JS檔案注入,在本域內
的a
內產生一個JS標籤,它的SRC指向請求的另外一個域的某個頁面b,b返回資料即可,可以直接返回JS的代碼。因為script的src屬性是可以跨域
的。具體看代碼,這個也比較簡單。

總結:
第一種情況:域和子域的問題,可以完全解決互動。
第二種情況:跨域,實現過程非常麻煩,需要兩個域開發人員都能控制,適用於簡單互動。
第三種情況:跨域,開發人員只控制一個域即可,實現過程需要增加代理取得資料,是常用的方式。
第四種情況:跨域,兩個域開發人員都需要控制,返回一段js代碼。

===================

A域有頁面a.html,其中有iframe包含B域的頁面b.html,現在要通過a.html上的一個按鈕,來把a.html頁面上一個文字框的值傳遞到b.html頁面的文字框。

註:
這裡b.html是html網頁,不能接收其他網站post過來的值,所以不能用直接post的方法來傳值,但是,如果接收頁面是b.aspx或者
b.asp
呢,那不是可以直接post了嗎?答案是肯定的,確實可以,但是b.asp或b.aspx必須要重新整理,才可以,如何能不重新整理的動態改變接收頁的元素或者值
呢?(IE的本地項目是可以實現跨域訪問的,但是外網的跨域訪問預設是被拒絕的。FireFox本地項目以及外網的跨域訪問都是被拒絕的。)

原理:

瀏覽器禁止跨域資料訪問,但是瀏覽器並沒有禁止跨域跨架構的post傳值。我們可以在A域,post到B域的某個頁面的架構中,然後通過B域的架構頁來實現本域內的資料訪問。這其實是html應用中的一個小技巧,並沒有用到其他高深的知識就實現了跨域的資料提交。

方法:

在B域中添加兩個頁面,來實現跨域的資料訪問,post.aspx和main.aspx。

頁面關係如下,A域的a.html包含一個架構,架構頁地址是B域的main.aspx,main.aspx是一個框架組包含兩個架構,(frmMain)b.html 和(frmPost)post.aspx.

A域的a.html:

<form action="http://www.b**.com/post.aspx" method="post" target="frmPost">

<input id="cmd" type="text" size="20">

<input type="submit">

</form>

<iframe src="http://www.b**.com/main.aspx"></iframe>

B域的main.aspx:

<frameset rows="*,0" frameborder="no" border="0" framespacing="0">

<frame src="b.html" name="frmMain">

<frame src="post.aspx" name="frmPost">

</frameset>

 

我們先把要傳遞到B域的資料儲存到a.html的form中,然後post到B域的post.aspx.

這時post.aspx接收到值,然後執行本域內的父架構訪問b.html。

string cmd = Request.Form["cmd"];

if (null != cmd && string.Empty != cmd)

{

       
Response.Write("<script language=/"JavaScript/" for=/"window/"
event=/"onload/"> if (parent && parent.frames[/"frmMain/"])
{ 這裡添加控制b.html的執行代碼} </script> ");

}

不難發現,這裡利用跳躍跨frame(即中間躍過了一層frame)的方法,來實現跨域的資料訪問。即post到frame的子frame裡面。

後記:

這個例子不過是一些特殊的情況下跨域訪問的解決方案,也許對你會有所協助。因為方法簡單,應用也就有很多局限性。(不過偶倒是覺得這樣很象ajax哦,頁面沒有重新整理,同樣完成了一次服務端的資料處理^o^)。

相關網文資料:

web應用的跨域訪問解決方案

做過跨越多個網站的Ajax開發的朋友都知道,如果在A網站中,我們希望使用Ajax來獲得B網站中的特定內容,如果A網站與B網站不在同一個域中,那麼就出現了跨域訪問問題。Ajax的跨域訪問問題是現有的Ajax開發人員比較常遇到的問題。

IE對於跨域訪問的處理是,彈出警告框,提醒使用者。如果使用者將該網站納入可信任網站,或者調低安全層級,那麼這個問題IE就不會在提醒你。

FireFox等其它非微軟的瀏覽器遇到跨域訪問,則解決方案統一是拒絕訪問。


人說,IE是主流瀏覽器,只要它能正常使用就好了。此言差已,IE雖然能夠處理,但是是有前提的,要麼使用者不厭其煩地在頁面彈出警告框之後點擊是(點擊否
就不執行該Ajax調用了),要麼使用者將該網站納入可信任網站。這兩種做法,在企業管理系統的應用中倒是比較常見,因為系統管理員可以以行政手段保證使用者
的行為。但是對於互連網上的網站或者門戶開發,這種做法則不行。

最近遇到了這個問題,需要在跨域訪問結束之後完成使主視窗出現一些特效,搜尋了一些資料,通過不斷嘗試以及在不同瀏覽器中進行相容性測試,找到了幾個可行的方案:

1、
Web代理的方式。即使用者訪問A網站時所產生的對B網站的跨域訪問請求均提交到A網站的指定頁面,由該頁面代替使用者頁面完成互動,從而返回合適的結果。此
方案可以解決現階段所能夠想到的多數跨域訪問問題,但要求A網站提供Web代理的支援,因此A網站與B網站之間必須是緊密協作的,且每次互動過程,A網站
的伺服器負擔增加,且無法代使用者儲存session狀態。

2、on-Demand方式。MYMSN的門戶就用
的這種方式,不過MYMSN中不涉及跨域訪問問題。動態控制script標記的產生,通過修改script標記的src屬性完成對跨域頁面的調用。此方案
存在的缺陷是,script的src屬性完成該調用時採取的方式時get方式,如果請求時傳遞的字串過大時,可能會無法正常運行。不過此方案非常適合聚
合類門戶使用。

3、iframe方式。查看過醒來在javaeye上的一篇關於跨域訪問的文章,他提到自己已
經用iframe的方式解決了跨域訪問問題。資料提交跟擷取,採用iframe這種方式的確可以了,但由於父視窗與子視窗之間不能互動(跨域訪問的情況
下,這種互動被拒絕),因此無法完成對父視窗效果的影響。

(偶找到了該文,補充一下地址:http://www.javaeye.com/topic/15641)

4、
使用者本地轉儲方式:IE本身依附於windows平台的特性為我們提供了一種基於iframe,利用記憶體來“繞行”的方案,即兩個window之間可以在
用戶端通過windows剪貼簿的方式進行資料轉送,只需要在接受資料的一方設定Interval進行輪詢,獲得結果後清除Interval即可。FF的
平台獨立性決定了它不支援剪貼簿這種方式,而以往版本的FF中存在的外掛程式漏洞又被fixed了,所以FF無法通過記憶體來完成暗渡陳倉。而由於檔案操作FF
也沒有提供支援(無法通過Cookie跨域完成資料傳遞),致使這種技巧性的方式只能在IE中使用。

5、我自
己用於解決這類問題的方式:結合了前面幾種方式,在訪問A網站時,先請求B網站完成資料處理,再根據返回的標識來獲得所需的結果。這種方法的缺點也很明
顯,B網站的負載增大了。優點,對session也實現了保持,同時A網站與B網站頁面間的互動能力增強了。最重要的一點,這種方案滿足了我的全部需要。

總結一下,以上方案中可選擇的情況下,我最推薦on-Demand方式,在不需要提交大量資料的情況下,這種方式能夠解決您的大部分問題。

 

===============================

本地站:http://www.somedomain.com

目標站:http://bbs.somedomain.com

解決方案 :

1. 在目標站 document.domain = 'somedomain.com';

並建立一個ajax.html,引用ajax方法 (大家都叫它服務中介)

然後建立一個ajax對象 var webreq = new Ajax();

2. 在本地站 document.domain = 'somedomain.com';

用iframe引用目標站的ajax.html.

 

詳細方法:

1. 本地站的一個頁面(Test.html)

html:

<html>

<head>

<script type='text/javascript'>document.domain='somedomain.com';</script>

<script type='text/javascript'>

function getAjax()

{

var spn = document.getElementById('spninfo');

var bbsWin = document.getElementById('ifrWindow').contentWindow;

var Ajax = bbsWin.webreq;

Ajax.Config.Result = 'TestAjaxCross';

Ajax.Config.returnType = 'Content';

Ajax.ActionPost('http://bbs.somedomain.com/doAjax.aspx',spn);

}

</script>

</head>

<body>

<span id='spninfo' />

<input type='button' id='ajaxBtn' onclick='getAjax' value='Get' />

</body>

<iframe id='ifrWindow' src='http://bbs.somedomain.com/ajax.html' style='display:none;'></iframe>

</html>

 

/////////////////////////////////////////////////////////////////////////////

 

2. 目標站的ajax.html

Html:

<html>

<head>

<script type='text/javascript'>document.domain='somedomain.com';</script>

<script type='text/javascript' scr='http://bbs.somedomain.com/ajaxMethod.js'></script>

<script type='text/javascript'>var webreq = new Ajax();</script>

</head>

<body>

</body>

</html>

 

----------------------------------------------------------------

目標站: doAjax.aspx

html為空白

doAjax.aspx.cs

代碼(Page_Load)

Response.Write("<zwd><content>看到效果了嗎?</content></zwd>");

return;

 

=====================================================

 

AjaxMethod.js 代碼

 

////////////////////Ajax////////////Class///////////////////
//Power By Gloot CopyRight @2006
//Edit Section

//Blog http://blog.sina.com.cn/tecz
//QQ 345268267
///////////////////////////////////////////////////////////

var Try = {
these: function() {
var returnValue;
for (var i = 0; i < arguments.length; i++) {
var lambda = arguments[i];
try {
returnValue = lambda();
break;
} catch (e) {}
}
//alert(123);
return returnValue;
}
}

function grr(rp) {
if(RegExp.$1)/(.*)/.exec("");
var re=new RegExp("<result>(.*)<//result>");
re.exec(rp);
if(RegExp.$1) return RegExp.$1;
return "";
}

function crr(rp) {
if(RegExp.$1)/(.*)/.exec("");
var re=new RegExp("<content>(.*)<//content>");
re.exec(rp);
if(RegExp.$1) return RegExp.$1;
return "";
}

var Ajax = function() {}
//var xhr ;這樣定義不行
Ajax.prototype.Init = function(){
return Try.these(
function() {return new ActiveXObject("Msxml2.XMLHTTP")},
function() {return new ActiveXObject("Microsoft.XMLHTTP")},
function() {return new XMLHttpRequest()}
) || false;
}

Ajax.prototype.Config = {
Result:"",
SucInfo:"",
FaildInfo:"",
Url:"",
returnType:"Compare", //輸入Compare是比較返回的字元是否一致,要指定Result值,//其他返回內容
ExecFunc:function(ty){
if (typeof ExecResult == 'function')
ExecResult(ty);
},
sendData:""
}

var aj = new Ajax();

Ajax.prototype.Action = function(url) { //同步
url = url + '&e='+Math.random();
var xhr = aj.Init();
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
if (xhr.status == 200)
{
aj.FuncResult(aj.Config.Spn,xhr);
}
}
}
xhr.open("POST",url,false);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");

xhr.send(null);

}

Ajax.prototype.Actionfor = function(url,spn) { //非同步

url = url + '&e='+Math.random();
var xhr = aj.Init(); //這樣寫是為了多非同步執行

xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
if (xhr.status == 200)
{
aj.FuncResult(spn,xhr);
}
}
}
xhr.open("GET",url,true);

xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send(null);
}

Ajax.prototype.ActionAlert = function(url) //執行alert提示框的同步
{
url = url + '&e='+Math.random();
var xhr = aj.Init(); //這樣寫是為了多非同步執行

xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
if (xhr.status == 200)
{
aj.AlertResult(xhr);
}
}
}
xhr.open("GET",url,false);

xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send(null);
}

Ajax.prototype.ActionPost = function(url,spn) //同步 send
{
url = url + '?e='+Math.random();
var xhr = aj.Init(); //這樣寫是為了多非同步執行

xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
if (xhr.status == 200)
{
aj.postResult(spn,xhr);
}
}
}

try {
if (netscape.security.PrivilegeManager.enablePrivilege)
{
netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
}
}catch(e) {};

xhr.open("POST",url,true);

xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
//xhr.setRequestHeader("Content-Length",pars.length);
xhr.setRequestHeader("Connection", "open");
xhr.send(Webreq.Config.sendData);
}

Ajax.prototype.GetJSONData = function(url,spn)
{
url = url + '?e='+Math.random();
var xhr = aj.Init(); //這樣寫是為了多非同步執行

xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
if (xhr.status == 200)
{
aj.jsonResult(spn,xhr);
}
}
}

xhr.open("POST",url,true);

xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
//xhr.setRequestHeader("Content-Length",pars.length);
xhr.setRequestHeader("Connection", "open");

xhr.send(null);
}

Ajax.prototype.onResult = function(v) {
return v==aj.Config.Result;
}

Ajax.prototype.FuncResult = function(spn,xhr)
{ //alert(spn.id);

if (aj.Config.returnType == 'Compare')
{
if (aj.onResult(grr(xhr.responseText)))
{

spn.innerHTML = aj.Config.SucInfo;
aj.Config.ExecFunc(aj.Config.Result);
if (aj.Config.Url!='')
{
window.location.href = aj.Config.Url;
}
}
else
{

spn.innerHTML = aj.Config.FaildInfo;

}
}
else
{
spn.innerHTML = crr(xhr.responseText);
aj.Config.ExecFunc(aj.Config.Result);
}

}

Ajax.prototype.AlertResult = function(xhr)
{
if (aj.Config.returnType=='Compare')
{
if (aj.onResult(grr(xhr.responseText)))
{
alert(aj.Config.SucInfo);
aj.Config.ExecFunc(aj.Config.Result);
}
else
{
alert(aj.Config.FaildInfo);
}
}
else
{
alert(crr(xhr.responseText));
aj.Config.ExecFunc(aj.Config.Result);
}
}

Ajax.prototype.postResult = function(spn,xhr)
{
if (aj.Config.returnType == 'Compare')
{
if (aj.onResult(grr(xhr.responseText)))
{
spn.innerHTML = aj.Config.SucInfo;
aj.Config.ExecFunc(aj.Config.Result);
}
else
{
spn.innerHTML = aj.Config.FaildInfo;
}
}
else
{
spn.innerHTML = crr(xhr.responseText);
aj.Config.ExecFunc(aj.Config.Result);
}
}

Ajax.prototype.jsonResult = function(spn,xhr)
{
var jsonstr = xhr.responseText;
var json = eval("return " + jsonstr);

//get data json.something json:{username:"123",content:""};

aj.Config.ExecFunc(aj.Config.Result);
}
///////////////////////////////////////////////////////////////

var WebServices = function() {}
WebServices.Config = {

}

相關文章

聯繫我們

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