Angular.js xhr.open()抛出'访问被拒绝'错误

时间:2014-11-05 18:26:20

标签: javascript ajax angularjs cors internet-explorer-10

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

然而,IE没有进行CORS预检,所以我认为我可以毫无问题地拨打这个电话。但是我得到了“访问被拒绝”。从角度代码深处抛出的错误。

进一步挖掘揭示出角度(v1.2.21)失败的特定线是:

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

在查看githubgoogle groupsstack 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文件中的代码,就可以为特定的salesforce调用返回该对象:

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)的行失败。有谁知道问题是什么?我很难相信角度没有办法处理IE中的跨域调用,所以我想我只是缺少一些东西。

1 个答案:

答案 0 :(得分: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 stackoverflow 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://stackoverflow.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 stackoverflow 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);
        });
}