最近做的一個項目中需要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 = {
}