使用JavaScript检查在线资源是否可访问,无需遵守同源策略的限制。

7
我希望能够使用JavaScript函数检查服务器是否“可访问”。此处的“可访问”表示如果服务器有响应,无论HTTP状态码是什么,都算可访问。我找到了一个Stackoverflow / Google推荐的函数:
function check(target)
{
    var xhr = new XMLHttpRequest();
    var target = "https://"+target+"/index.php";
    var num = Math.round(Math.random() * 10000);

    xhr.open("HEAD", target + "?num=" + num, true);
    xhr.send();
    xhr.addEventListener("readystatechange", processRequest, false);

    function processRequest(e)
    {
      if (xhr.readyState == 4)
      {
        if (xhr.status !== 0)
        {
          return true;
        }
        else
        {
          return false;
        }
      }
    }
}

如果目标允许使用Access-Control-Allow-Origin: *(或特定客户端),那么它就可以正常工作。但情况并非如此。

我发现了很多解决方案,它们似乎都依赖于这一点。

如何在JavaScript中检查服务器是否可达,与服务器上的Access-Control-Allow-Origin设置无关呢?

编辑:我想补充一下,我不能修改目标(例如,更改头部),但我能够识别应该可用的资源(例如,https://target/this-specific-site.php

编辑2:我正在尝试实现@Vic建议的解决方案; 在这里尝试

function chk(){

var img = new Image();   // Create new image element

img.onload = function(){
    return false;
};

img.onerror = function() {
    return true;
};

// Try to change this URL to nonexistant image
img.src = 'https://www.google.com/images/srpr/logo3w.png'; // Set source path

}

if (chk())
{
alert("IMAGE LOADED");
}
else
{
alert("IMAGE FAILED TO LOAD ("+chk()+")");
}

但是该函数似乎返回undefined,所以检查失败。 编辑3: 我需要该函数给我一个返回值。

(1)从网站获取一张图片 (2)在您的网站上加载它 (3)如果发生错误,请假设该网站已关闭。 - Vic
@Quentin,如果您尝试将脚本的目标设置为https://enable-cors.org/,我认为您会发现页面确实会返回0 - SaAtomic
@Vic,我正在尝试借助另一篇Stackoverflow帖子来实现这个。 但对我来说,无论是失败还是加载,两个函数都会被执行。 请参见:https://jsbin.com/worutemobe/edit?html,console,output - SaAtomic
刚刚检查了你的代码... 不是那样工作的,哈哈 - Vic
给你,兄弟。https://jsbin.com/temuniwuzi/1/edit - Vic
谢谢,我已经让它工作到了这个程度,但我需要它根据结果返回truefalse - SaAtomic
5个回答

15

在现代浏览器中,您可以使用Fetch API及其no-cors请求模式,该模式将返回一个opaque响应(您不会能够从中完成任何其他操作):

fetch('http://google.com', {mode: 'no-cors'}).then(r=>{
  console.log('google is reachable');
  })
  .catch(e=>{
    console.log('google is not there');
    });
fetch('http://fakeservertahtdoesntexist.com', {mode: 'no-cors'}).then(r=>{
  console.log('fake is reachable');
  })
  .catch(e=>{
    console.log('fake is not there');
    });

奇怪的是,我的火狐浏览器没有抛出异常(但也没有执行),而我的谷歌浏览器却抛出了异常。


1
这看起来非常有前途!我尝试实现它,但是我得到了undefined的返回值。请参见https://jsbin.com/hadoluqire/edit?html,console,output - SaAtomic
2
现在你必须理解异步脚本编程(无论你使用什么技巧,都必须是异步的)。 - Kaiido
1
哎呀,这比我想象的要复杂得多了。不过还是谢谢你! - SaAtomic
1
请注意,fetch()是一项实验性功能:“请注意实验性技术的语法和行为可能会发生变化”(来源:https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch) - Perspectivus
3
@Perspectivus fetch被列入《Living Standards》中,我认为它不会很快发生重大变化。一些方法尚未被所有UA实现,但同样适用于许多API(这些API也将具有默认的MDN通知)。 - Kaiido

3
您可以添加一个带有src属性的脚本标签,引用服务器上已知存在的文件,并使用onerror事件来检测它是否未找到:
<script src="nonexistent.js" onerror="alert('error!')"></script>

感谢最后一个评论在这个答案中的贡献。

使用这个解决方案,您不应该遇到跨域问题。

更新

如果您不想冒险运行脚本,可以使用img标签。使用onerror事件来检测失败,并使用onload事件来检测成功:

<html>
    <head>
        <img src="https://www.google.com/images/srpr/logo3w.pngx" onerror="failure()" onload="success()" height="0" width="0">
        <script>
            let _isSuccess = false;
            function success() {
                _isSuccess = true;
            }

            function failure() {
                _isSuccess = false;
            }

            function isSuccess() {
                console.log(`is success == ${_isSuccess}`);
                return _isSuccess;
            }

        </script>
    </head>
    <body onload="isSuccess()">
        <h1>Hello World!</h1>
    </body>
</html>

2
难道它不和加载外部图片一样吗?加载脚本有点可怕。 - Vic
1
这似乎是一个非常相似的选项,但我不知道如何在函数中使用它来给我falsetrue - SaAtomic
1
@SaAtomic,为避免异步问题,请在<body>标签的onLoad()事件中调用isSuccess()函数。 - Perspectivus

2

这个可以吗?异步函数(onerror/onload)无法返回值。相反,您可以使用类似回调的方式。

function conditional(status){
  if(status)
  {
    console.log("Hey, the image loaded correctly.");
  }
  else
  {
    console.log("Bruh, the server is dead.");
  }
}

var tester = new Image();
tester.onload = function() {
  conditional(true);
};
tester.onerror = function() {
  conditional(false);
};
tester.src="https://i.imguhr.com/Ru1q3sS.jpg";

https://jsbin.com/veruzegohe/1/edit?html,console,output


这很不错。对我来说似乎完美无缺。感谢帮助 JS 新手。 - SaAtomic
研究一下回调函数,兄弟。它会改变你的JS生活。顺便问一下,我有回答你的问题吗?(我可以这么说吗?抱歉,我是SO新手) - Vic
我还有点迷茫,正在尝试在我的脚本中实现这个。我不明白如何将其用作返回true或false的函数,以便我可以在另一个函数中执行if(check()){[...];} - SaAtomic
哥们,你不能异步地执行返回操作。IF语句需要同步函数。我建议将你的脚本放在条件函数内部。 - Vic
这是一个很棒的小技巧,用于确定 Web 服务器是否正常运行/可访问。 - Fat Monk
我尝试过这个,但有时浏览器会从缓存中提供图像 :-/ - AndreLung

0
这是不可能的。这将允许被同源策略所保护的信息泄漏。
例如,用户所在公司是否有特定URL的内部网页?
这可能导致诸如确定公司使用哪个版本的Microsoft Sharepoint(以一个众所周知的例子为选),它安装在哪个URL上,然后利用这些信息进行社交工程攻击等问题。

1
这应该是可以实现的,正如@Vic所建议的那样,我可以尝试从服务器检索图像并查看是否加载。 - SaAtomic
1
@SaAtomic 你也可以通过代理运行。让 PHP 发送 GET 请求。 - Vic
4
实际上,他们发明了 opaque 响应和 no-cors 请求模式,这被认为足够安全,因为只允许请求使用 HEAD、GET 或 POST。如果我理解正确,它避免了 HEAD + GET 或 HEAD + POST,但我不确定我是否理解正确,也不确定它如何避免端口嗅探。 - Kaiido

0
这是我做的方法:
  AbortSignal.timeout ??= function timeout(ms) {
    const ctrl = new AbortController();
    setTimeout(() => ctrl.abort(), ms);
    return ctrl.signal;
  };

  fetch("https://google.com", { signal: AbortSignal.timeout(2000), mode: "no-cors" })
    .then((r) => {
      console.log("google is reachable");
    })
    .catch((e) => {
      console.log("google is not there");
    });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接