如何在Javascript中检测XMLHttpRequest()的其他类型的错误与交叉原点(CORS)错误

时间:2013-10-11 18:56:51

标签: javascript ajax xmlhttprequest cors

我正在尝试检测XMLHttpRequest()由于交叉原始错误而不是错误请求而失败的时间。例如:

    ajaxObj=new XMLHttpRequest()
    ajaxObj.open("GET", url, true); 
    ajaxObj.send(null);

考虑4个网址:

案例1: url是正确设置access-control-allow-origin的有效地址

  • 示例:http://192.168.8.35我在标题
  • 中设置了Access-Control-Allow-Origin: *的服务器
  • 这很容易被检测为ajaxObj.readyState == 4和ajaxObj.status == 200

案例2: url是现有服务器上的无效地址

  • 示例:http://xyz.google.com服务器响应,但它不是有效请求
  • 这导致ajaxObj.readyState == 4和ajaxObj.status == 0

案例3: url是一个不存在的服务器IP地址

  • 示例:我本地网络上没有任何回复的http://192.168.8.6
  • 这导致ajaxObj.readyState == 4和ajaxObj.status == 0

案例4: url是一个有效的地址,其中access-control-allow-origin为 NOT set

  • 示例:http://192.168.8.247我在服务器中没有 Access-Control-Allow-Origin: *设置在标题中
  • 这导致ajaxObj.readyState == 4和ajaxObj.status == 0

问题是:如何区分案例4(访问控制允许来源错误)和案例2&3?

在案例4中,Chrome调试控制台显示错误:

XMLHttpRequest cannot load http://192.168.8.247/. Origin http://localhost is not allowed by Access-Control-Allow-Origin.

如何在Javascript中识别该错误?

我试图在ajaxObj中找到一些指示,但与案例2和3相比似乎没有什么不同。

这是我使用的简单测试:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CORS Test</title>
<script type="text/javascript">
function PgBoot()
{
//  doCORS("http://192.168.8.35");   // Case 1
//  doCORS("http://xyz.google.com"); // Case 2
    doCORS("http://192.168.8.6");    // Case 3
//  doCORS("http://192.168.8.247");  // Case 4
}

function doCORS(url)
{
    document.getElementById("statusDiv").innerHTML+="Processing url="+url+"<br>";
    var ajaxObj=new XMLHttpRequest();
    ajaxObj.overrideMimeType('text/xml');
    ajaxObj.onreadystatechange = function()
    {
        var stat=document.getElementById("statusDiv");
        stat.innerHTML+="readyState="+ajaxObj.readyState;
        if(ajaxObj.readyState==4)
            stat.innerHTML+=", status="+ajaxObj.status;
        stat.innerHTML+="<br>";
    }
    ajaxObj.open("GET", url, true); 
    ajaxObj.send(null);
}
</script>
</head>
<body onload="PgBoot()">
<div id="statusDiv"></div>
</body>
</html>

使用Chrome的结果:

Processing url=http://192.168.8.35
readyState=1
readyState=2
readyState=3
readyState=4, status=200

Processing url=http://xyz.google.com
readyState=1
readyState=4, status=0

Processing url=http://192.168.8.6
readyState=1
readyState=4, status=0

Processing url=http://192.168.8.247
readyState=1
readyState=4, status=0

4 个答案:

答案 0 :(得分:39)

不,根据W3C Spec。

,无法区分

以下是CORS规范如何指定simple cross-origin request过程:

  

在提出请求时应用make a request步骤并遵守下面的请求规则。

     

如果未设置手动重定向标志且响应的HTTP状态代码为301,302,303,307或308:应用重定向步骤。

     

如果最终用户取消请求:应用中止步骤。

     

如果出现网络错误: 如果出现DNS错误,TLS协商失败或其他类型的网络错误,应用网络错误步骤< / strong>即可。不要求任何类型的最终用户互动...

     

否则:执行资源共享检查。 如果失败,请应用网络错误步骤 ...

如果网络连接失败或CORS交换失败,则会应用network error steps,因此无法区分这两种情况。

为什么呢?一个好处是它可以防止攻击者检查LAN的网络拓扑。例如,恶意网页脚本可以通过请求其HTTP接口找到路由器的IP地址,从而了解有关网络拓扑的一些信息(例如,您的私有IP块有多大,/8或{{ 1}})。由于您的路由器没有(或不应该)发送CORS标头,因此脚本完全没有学到任何东西。

答案 1 :(得分:7)

也许万一它可以帮助任何人...另一种方法来处理cors和网络错误之间的差异......可以使用chrome或firefox ......(虽然不是完美的解决方案)

var external = 'your url';

if (window.fetch) {
    // must be chrome or firefox which have native fetch
    fetch(external, {'mode':'no-cors'})
        .then(function () {
            // external is reachable; but failed due to cors
            // fetch will pass though if it's a cors error
        })
        .catch(function () {
            // external is _not_ reachable
        });
} else {
    // must be non-updated safari or older IE...
    // I don't know how to find error type in this case
}

答案 2 :(得分:2)

令人惊讶的是,您可以对图像执行此操作(对于其他特定的内容类型,可能有类似的解决方案)。诀窍是显然不考虑CORS,因此语句“ URL无法通过XHR加载,&& URL成功通过img src加载” 暗示该URL有效,但被CORS阻止了。

这绝对不能在所有情况下都有用,但是在某些情况下(例如,实用程序检测您是否正确传递了CORS),可以解决问题。例如,如果我的应用程序(前端和后端分别安装)的后端URL配置正确且服务器上的CORS配置错误,则我想在控制台中发出警告,因此对我来说这已经足够了,因为我可以提供图像进行测试上。

function testCors(url) {
    var myRequest = new XMLHttpRequest();
    myRequest.open('GET', url, true);
    myRequest.onreadystatechange = () => {
        if (myRequest.readyState !== 4) {
            return;
        }
        if (myRequest.status === 200) {
            console.log(url, 'ok');
        } else {
            var myImage = document.createElement('img');
            myImage.onerror = (...args) => {console.log(url, 'other type of error', args);}
            myImage.onload = () => {console.log(url, 'image exists but cors blocked');}
            myImage.src = url;
            console.log(url, 'Image not found');
        }
    };
    myRequest.send();
}

答案 3 :(得分:0)

这就是我所做的-尽管它确实需要您对远程服务器进行更改(向其中添加页面)。

  1. 我创建了一个probe.html页面以在iframe中投放(在您尝试使用CORS进行联系的远程起源上)
  2. 我在页面中注册了一个window.onmessage处理程序
  3. 我使用安全的iframe将probe.html页面加载到页面中
  4. 在我的iframe onload事件页面中,我使用window.postMessage()将消息发送到iframe
  5. probe.html页面可探查url(来自iframe的同一来源)
  6. probe.html页面使用window.postMessage()向我发送xhr.status和xhr.statusText响应详细信息
  7. 如果状态为错误,我会将状态和statusText记录到我们的日志记录服务中

但是,请注意,以任何一种方式传递参数都很难保证其安全性(任何人都可以嵌入您的iframe,其他各方可能导致postMessage到达页面或iframe)。

它不是完美的(它只检测不是短暂的一次错误)。

尽管工作量很大,但是您可以获得有关错误原因的更多信息。当您无法直接访问用户的浏览器时非常有用。

PS:这与PHP答案完全不同,后者表明您的服务器应与远程服务器进行对话(a)用于诊断通信失败原因的方法不多,以及(b)使用PHP的curl只是要求您的服务器将被认真伪装!