Angular.js xhr.open() 抛出“拒绝访问”错误

Posted

技术标签:

【中文标题】Angular.js xhr.open() 抛出“拒绝访问”错误【英文标题】:Angular.js xhr.open() throws 'Access is Denied' error 【发布时间】:2015-01-02 01:41:24 【问题描述】:

我正在开发一个必须支持 IE10 的 Angular Web 应用程序。我需要对我们的企业 salesforce 服务器进行跨域调用。在 chrome(我们不正式支持,但我们都在其中进行开发)中,该调用失败,因为 chrome 将 OPTIONS 预检调用发送到不支持 CORS 的 salesforce 服务器。

但是,IE 不会进行 CORS 预检,所以我认为进行此调用不会有任何问题。但我收到“拒绝访问”。从 Angular 代码深处抛出的错误。

进一步挖掘显示,失败的 angular (v1.2.21) 中的特定行是:

xhr.open(method, url, true); (on line 8544 if you happen to have version 1.2.21).

在查看github、google groups 和stack overflow 线程时,我发现问题可能出在 IE 想要处理跨域请求的方式上,特别是调用哪个 xhr 对象来进行调用。

似乎旧版本的 Angular 存在此问题,但已通过在 xhr.open() 调用之前添加一个函数来为正在运行的 IE 版本检索正确的 XMLHttpRequest 对象来解决:

var xhr = createXhr(method);

xhr.open(method, url, true);
forEach(headers, function(value, key) 
  if (isDefined(value)) 
      xhr.setRequestHeader(key, value);
  
);

所以理论上,正确的 xhr 对象是调用了它的 .open() 方法。但是对我来说,该行会引发“访问被拒绝”错误。

在上面的链接中,似乎通常建议不要使用XMLHttpRequest 对象进行跨域调用,而必须使用XDomainRequest()。考虑到有角度的人不太可能错过这一点,我还是尝试了,只是手动更改 angular.js 文件中的代码以返回该对象以用于我们特定的销售人员调用:

var xhr;
if (url.indexOf("salesforce.com") > -1) 
  xhr = new XDomainRequest();

else 
  xhr = createXhr(method);


xhr.open(method, url, true);
forEach(headers, function(value, key) 
  if (isDefined(value)) 
      xhr.setRequestHeader(key, value);
  
);

除了现在代码尝试调用xhr.setRequestHeader(key, value) 的行失败。有谁知道问题是什么?我很难相信 Angular 没有办法处理 IE 中的跨域调用,所以我想我只是遗漏了一些东西。

【问题讨论】:

你在混合http和https吗? @epascarello 是的。我们的地址是http://...,销售人员地址是https://... 但是,如果我将他们的地址更改为http,我仍然会遇到同样的问题。访问被拒绝,实际上没有致电 salesforce。 那将是你的问题。 blogs.msdn.com/b/ieinternals/archive/2010/05/13/… @tengen 看起来像是一个普通的 CORS 问题。如果没有 CORS 支持,您将无法向所述服务器发送 xhr 请求。 @epascarello 该链接很容易成为我见过的对 CORS 的最佳、最易读的解释,而且我也看到了我的公平份额。谢谢! +1!我在文章中看到了一个更新:更新:Internet Explorer 10 现在支持使用 XMLHTTPRequest 的 CORS,这应该比现在已弃用的 XDomainRequest 对象更受欢迎。 这解释了为什么 XDomainRequest 对我来说不是问题在 IE10 中。但即使我对 HTTP 进行 GET 调用(删除那篇文章中 #7 中的问题...混合 http/https 请求)我仍然遇到同样的问题。 【参考方案1】:

虽然您在这里专门说 IE10,但我在 IE8-9 上遇到了同样的问题。我的解决方案是使用跨域对象 window.XDomainRequest。

function loadCaptions() 

    //Use the XDomainRequest if it is defined (IE8-10), or when angular calls .open on the xhr request will receive "access denied" error.
    var url = $scope.captionsUrl;
    if (!!window.XDomainRequest) 
        var xdr = new window.XDomainRequest();
        if (xdr) 
            xdr.open("get", url);
            xdr.send();
        

        return;
    


//You folks on *** can ignore this part, just left in to show how I was requesting the captions leading to "access denied"
    $http.get($scope.captionsUrl)
        .success(function (captionsJson) 
            $scope.captionsList = captionsJson;
            createCaptionsMap();
        )
        .error(function (data, status, headers, config) 
            $cbtErrors.failedToLoadCaptions($scope.captionsUrl);
        );

编辑:

这是一个更完整的解决方案,其中包括由于 XDR 请求中的错误和“成功”/“错误”回调导致的内存管理:

function loadCaptions() 
    //Use the XDomainRequest for IE8-9, or angular get request will recieve "access denied" error.
    var url = $scope.captionsUrl;

    if (!!window.XDomainRequest) 
        var xdr = new window.XDomainRequest();
        //See explination below why global.pendingXDR is set.  Cleaning up that memory here.
       var removeXDR = function(xdr) 

            //You will need a indexOf function defined for IE8.  See http://***.com/questions/3629183/why-doesnt-indexof-work-on-an-array-ie8.
            var index = global.pendingXDR.indexOf(xdr);
            if (index >= 0) 
                global.pendingXDR.splice(index, 1);
            
        ;

        if (xdr) 
            //bind xdr.onload before sending the request (or the event does nothing).
            xdr.onload = function()
                removeXDR(xdr);
                $scope.captionsList = xdr.responseText;
                createCaptionsMap();
            ;
            xdr.onerror = function()
                removeXDR(xdr);
                $cbtErrors.failedToLoadCaptions($scope.captionsUrl);
            ;
            xdr.open("get", url);
            xdr.send();

            //In Internet Explorer 8/9, the XDomainRequest object is incorrectly subject to garbage collection after
            //send() has been called but not yet completed. The symptoms of this bug are the Developer Tools'
            //network trace showing "Aborted" for the requests and none of the error, timeout, or success event
            //handlers being called.
            //To correctly work around this issue, ensure the XDomainRequest is stored in a global variable until
            //the request completes.
            global.pendingXDR = [];
            global.pendingXDR.push(xdr);

        

        return;
    


//You folks on *** can ignore this part, just left in to show how I was requesting the captions leading to "access denied"
    $http.get($scope.captionsUrl)
        .success(function (captionsJson) 
            $scope.captionsList = captionsJson;
            createCaptionsMap();
        )
        .error(function (data, status, headers, config) 
            $cbtErrors.failedToLoadCaptions($scope.captionsUrl);
        );

【讨论】:

我可以建议您在其中添加 internet-explorer-8 和 9 标签吗?以及标题中的 Internet Explorer 和 cors。这些东西会帮助我更快地找到问题。

以上是关于Angular.js xhr.open() 抛出“拒绝访问”错误的主要内容,如果未能解决你的问题,请参考以下文章

AJAX之XHR

小白学AJAX-02-原理

原生 ajax

XHR post请求下载文件

javascript:Ajax与Comet

Ajax