http://www.cftea.com/c/2008/06/9QA4QM0238B2C0SF.asp
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>
<div id=dv></div>
<input onclick=loadContent() type=button value="Click Me">
其中的 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 的内容。
编者注:如果 Ajax 的 js 内容由 B 提供,则 A 可以利用 B 提供的 js 方便地访问 B 的资源。比如 A 中的 js 代码:
<script type="text/javascript" src="B/core.js"></script>
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="发送" onclick="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="发送" onclick="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
相关阅读
* http://www-128.ibm.com/developerworks/library/x-securemashups/