使用动态的script标签发起Web Services请求
XmlHttpRequest
|
Dynamic script tag
|
|
跨浏览器兼容性
|
No
|
Yes
|
强制的跨域浏览器安全
|
Yes
|
No
|
是否能接收HTTP状态码
|
Yes
|
No (fails on any HTTP status other than 200)
|
是否支持HTTP GET和POST
|
Yes
|
No (GET only)
|
能否发送/接收HTTP头
|
Yes
|
No
|
能否接收XML
|
Yes
|
Yes (but only embedded in a JavaScript statement)
|
能否接收JSON
|
Yes
|
Yes (but only embedded in a JavaScript statement)
|
是否提供同步和异步调用
|
Yes
|
No (asynchronous only)
|
配置Apache Web服务器使Apache模块能为Ajax域限制提供一个解决方案。
你也许已经知道,到目前为止,XMLHttpRequest不能自动跨域运行。例如,当你下载一个Web页面时,你不能使用Request对象对不同于那个Web页面所在域的其他域发起请求。幸运的是,有一个简单的解决方案能避免此限制——Apache Web服务器的mod_rewrite。“这个模块使用基于规则的重写引擎(基于一个正则表达式的解析器)来重写被请求的URL,”按照Apache的在线文档所说(可见http://httpd.apache.org/docs/1.3/- mod/mod_rewrite.html)。
跨域障碍
在我们阅读这个解决方案的描述之前,让我们来看看一系列示范性的函数,它们完成了XMLHttpRequest的相关工作:
function getXmlHttpObject(){
if (window.XMLHttpRequest)
return new XMLHttpRequest();
else if (window.ActiveXObject)
return new ActiveXObject("Microsoft.XMLHTTP");
else {
alert("XMLHttpRequest not supported!");
return null;
}
}
function handleHttpResponse() {
if (http.readyState == 4) {
results = http.responseText;
alert(results);
}
}
function doSomeStuff() {
var post_arg1 = document.my_form.post_arg1.value;
var post_arg2 = document.my_form.post_arg2.value;
var post_url = 'http://yahoo.com/form_do'
post_data = 'post_arg1=' + post_arg1 + '&post_arg2=' + post_arg2;
http.open("POST", post_url);
http.setRequestHeader('Content-Type',
'application/x-www-form-urlencoded; charset=UTF-8');
http.send(post_data);
http.onreadystatechange = handleHttpResponse;
return false;
}
var http = getXmlHttpObject();
你所见到的三个函数中的最后一个将被调用来执行HTTP请求。
现在,假设这个脚本在一个URL为http://premshree.org/form的HTML文件中。表单中的某些事件处理器(onBlur、onClick、onSubmit等)触发doSomeStuff(),它接下来发起一个请求到另一个域(yahoo.com)中的form_do。
注意包含表单和JavaScript的HTML文件域和执行动作(http://yahoo.com/ form_do)的文件域之间的不匹配之处。域的不匹配就是问题之源。
跨域的XMLHttpRequest工作
IE和基于Mozilla的浏览器处理跨域请求有所不同。你可以在IE中执行跨域请求;然而,这需要改变浏览器默认的安全设置,或者添加特定的主机到你的“信任主机”列表中。可见http://msdn.microsoft.com/msdnmag/issues/02/06/web/:
由于没有一种方法指定哪些页面应该信任其他页面来访问它们的数据,Internet Explorer简单地规定如果两个页面不在同一个域中,它们就不能通信。更确切地说,Zone Manager(可在IE设置的安全项里找到)允许用户设置一个页面可以访问另一个页面,但是如你所知,大多数用户并不做设定,而只是在弹出窗口时才设置。你可以建议用户将页面添加到信任站点区域,或者仅仅在对话框中点Yes……
另一方面,在Mozilla中有签名脚本的概念(可见http://www.mozilla.org/projects/- security/components/signed-scripts.html)。在基于Mozilla的浏览器中,根据包含在跨域请求中的不同域,你应该赋予一个或更多的UniversalBrowser权限。例如,如果你正从本地文件系统访问一个远程主机,也就是说从file://访问http://,你应该开启UniversalBrowserRead权限。
现实的情形是,跨域的XMLHttpRequest请求在你真正在乎的浏览器中并不能
按照你想要的运行方式工作(当然除非你足够疯狂,强迫天真的、信任你的用户处理诸如签名脚本以及信任主机这样的事情)。
存在解决方案吗
是的,感谢一些mod_rewrite的魔法。你所需要的就是RewriteRule指令。
需要对Apache配置文件做出一些配置变化(通常是httpd.conf)。下面就是包含的步骤:
1. 配置Apache,开启proxy:
./configure –enable-proxy
2. 确信开启了RewriteEngine:
RewriteEngine on
3. 添加以下规则:
RewriteRule ^/form_do$ http://yahoo.com/form_do [p]
这里你所见的P标志表示一个pass-through proxy。
提示:可见http://www.google.com/search?q=pass-through+ proxying。
现在,在JavaScript代码中不再请求http://yahoo.com/form_do,而是使用URL /form_do。请求代码如下所示:
var post_url = '/form_do';
就这样——你完成了!感谢Gopan以及http://t3.dotgnu.info为本hack提供了大量信息。
——Premshree Pillai