最近做的一个项目中需要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在其中充当了代理的作用。具体可以看下我的代码。
四、这个和上个的区别就是请求是使用<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 = {
}
?