我正在尝试在IE上做跨域。
我使用XDomainRequest,并植入所有事件的日志记录(onerror, onload, onprogress和ontimeout)来监控进度。
它有时工作,但不是总是(一台电脑,IE9,同一个网站,相同的请求,3或4个中的一个工作;另一台电脑,IE8,也许1/2就可以了)。我没有从日志中得到任何有用的信息,因为没有触发任何东西。
我很困惑。有IE的调试工具吗?为什么有时候XDomainRequest不工作?
非常感谢coronin
XDomainRequest对象中至少有两个重要的bug,一个影响IE8,另一个影响IE9。
Issue 1 -垃圾回收
在Internet Explorer 8中,在send()被调用但尚未完成之后,XDomainRequest对象不正确地受到垃圾收集的影响。此错误的症状是开发人员工具的网络跟踪显示请求"中止",并且没有调用任何错误、超时或成功事件处理程序。
典型的AJAX代码看起来像这样:
function sendCrossDomainAjax(url, successCallback, errorCallback) {
var xdr = new XDomainRequest();
xdr.open("get", url);
xdr.onload = function() { successCallback(); }
xdr.onerror = function() { errorCallback(); }
xdr.send();
}
在这个例子中,包含XDomainRequest的变量超出了作用域。如果用户不走运,IE的Javascript垃圾收集器将在send()异步完成之前运行,请求将被中止。即使XDomainRequest对象可以被捕获到OnLoad和OnError事件处理程序中,IE也会看到整个对象图没有对它的引用,并将对它进行垃圾收集。IE应该"固定"对象,直到完成。
你会注意到互联网上有很多其他的讨论提到在xdr.send();调用将以某种方式"解决"神秘的XDomainRequest失败。这是一种拼凑,而且完全错误。所发生的一切都是XDomainRequest对象被"固定"到setTimeout闭包中,而不是像以前那样快速地进行垃圾收集。这解决不了问题。
要正确解决此问题,请确保在请求完成之前将XDomainRequest存储在全局变量中。例如:
var pendingXDR = [];
function removeXDR(xdr) {
// indexOf isn't always supported, you can also use jQuery.inArray()
var index = pendingXDR.indexOf(xdr);
if (index >= 0) {
pendingXDR.splice(index, 1);
}
}
function sendCrossDomainAjax(url, successCallback, errorCallback) {
var xdr = new XDomainRequest();
xdr.open("get", url);
xdr.onload = function() {
removeXDR(xdr);
successCallback();
}
xdr.onerror = function() {
removeXDR(xdr);
errorCallback();
}
xdr.send();
pendingXDR.push(xdr);
}
Issue 2 -缺少OnProgress EventHandler
第二个问题是已知的。Internet Explorer 9在XDomainRequest对象中引入了一个回归,当它试图报告进度信息时,缺少(null) OnProgress事件处理程序将导致请求中止。
对于快速请求,IE9从不尝试调用OnProgress事件处理程序,请求成功。在某些情况下,如由于打开的连接太多、网络延迟、服务器响应慢或请求或响应负载大而导致IE延迟请求时,将导致IE9开始报告进度信息。
IE9试图调用事件处理程序而不首先检查它是否存在,并且XDomainRequest对象崩溃并在内部销毁自己。
要解决此问题,请始终确保将事件处理程序附加到OnProgress。考虑到这个bug,防御性地为所有对象的事件添加事件处理程序并不是一个坏主意。
var xdr = new XDomainRequest();
xdr.open("get", url);
xdr.onprogress = function() { };
// regsister other event handlers
我似乎报告说,如果事件处理程序在.open()调用之前注册,XDomainRequest可能会失败。同样,从防御角度来说,在.open()和.send()调用之间注册它们并不是一个坏主意。我还没有亲自验证这是否是一个真正的bug。
如果你遇到一个"Access Denied"错误,这是因为XDomainRequest不允许目标和主机页面之间不匹配的URI方案。换句话说,不要从HTTPS页面调用HTTP资源。
小心互联网上的大多数XDomainRequest库。我研究了大多数流行的插件,比如各种jQuery AJAX传输插件(包括这里另一个答案中链接的插件)。
当然,XDomainRequest受到所有正常的限制和约束。这些本身并不是bug,并且与替代方案(iframe kludges, Flash crossdomain.xml传输)相比,它们并不像那么糟糕。
我在公共领域许可下发布了一个新的jQuery AJAX XDomainRequest传输:https://github.com/ebickle/snippets/tree/master/javascript/xdomainrequest
也有同样的问题。简短的解决方案:
- 使用此代码:https://github.com/jaubourg/ajaxHooks/blob/master/src/ajax/xdr.js
更新:链接断开,在这里找到jaubourgs修复:https://github.com/jaubourg/ajaxHooks/blob/master/src/xdr.js
- 添加
xdr.onprogress = function() {};
到xdr.js文件
详情可以在jQuery主题讨论中找到
http://bugs.jquery.com/ticket/8283,其中最后一个回复包含该xdr。Onprogress修复源自于这个bug的讨论,标题为
"如果没有指定所有事件处理程序,IE9 RTM - XDomainRequest发出的请求可能会中止"http://social.msdn.microsoft.com/forums/en us/iewebdevelopment/thread/30ef3add - 767 - c - 4436 b8a9 f1ca19b4812e