在浏览器或 Node.js 中使用 JavaScript 的 Fetch 测试网站连通性

自己有在浏览器测试网站连通性的需求,最后使用 Fetch 实现了,就在这里分享一下中间遇到的坑和最后的解决方案,不想看前面废话可以直接跳转文末查看代码

XHR

最开始打算直接用 XMLHttpRequest 解决,但是浏览器遇到了跨域拦截(CORS 失败)

  • js
1
2
3
4
5
let xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.google.com', !0);
// async 参数被设置为 !0
// !0 在 JS 中是 true 的一种简写
xhr.send(null);

XHR CORS

出现 Access to XMLHttpRequest at (url) from origin (src) has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. 报错,是因为待测的目标 URL(在这个示例中是 https://www.google.com )没有允许浏览器读取响应信息

正常情况下,xhr.status 应该会给我们响应的状态码,浏览器此时已经成功收到了 302 重定向的响应,但是 xhr 请求出现了 CORS 错误,再尝试获取这个状态码只能得到 0
也就是说,无论是否可以访问待测网站,xhr.status 都只会返回 0

在浏览器中,XHR 请求都受到 CORS 策略的限制,而 XHR 也无法使用 no-cors 模式来绕过这个限制,这个时候,我们就需要使用 Fetch

Fetch

FetchXMLHttpRequest 更现代的替代品,是 ES6(ECMAScript 2015)的东西(也就是说不支持 IE)

除了过时的 Internet Explorer 和 Windows Script Host,基本所有可以执行 JS 的地方都能使用 Fetch,你甚至可以在 Node.js 中使用它

先用 Fetch 重写下文章开头的代码

  • js
1
2
3
4
fetch("https://www.google.com", {
method: "GET",
body: null,
})

Fetch CORS

同样会获得 CORS 错误,提示 Failed to fetch,注意看,这个错误属于 TypeError

但是我们可以让 Fetch 使用 no-cors,来绕过这个 CORS 限制!

改进一下代码,然后观察 Fetch 出现的 Error:

  • js
1
2
3
4
5
6
7
8
9
10
11
var request = fetch("https://www.google.com", {
method: "GET",
body: null,
mode: 'no-cors'
})
request.then( res => {
console.log(res)
return res.json()
})
.catch(error => console.log('Error:', error))
.then( response => console.log('Success:', response));

执行这段代码,我们会得到 Error: SyntaxError: Unexpected end of input

为什么会得到 SyntaxError 呢?因为 no-cors 会阻止浏览器获取响应的内容

但实际上,如果我们得到了 SyntaxError,则说明这个请求已经发送成功,并且接收到了响应

让我们将测试 URL 改为一个不存在的地址,再试一遍:

  • js
1
2
3
4
5
6
7
8
9
10
11
var request = fetch("https://this.domain.does.not.exist", {
method: "GET",
body: null,
mode: 'no-cors'
})
request.then( res => {
console.log(res)
return res.json()
})
.catch(error => console.log('Error:', error))
.then( response => console.log('Success:', response));

执行代码,我们会得到 Error: TypeError: Failed to fetch

也就是说,带上 no-cors 之后,虽然请求成功也会报错,但是出现的是另一个报错

  • 请求成功:SyntaxError
  • 请求失败:TypeError

接下来,对着不同错误做不同处理,就可以检测啦

解决方案

直接放代码:

  • js
1
2
3
4
5
6
7
8
9
10
11
var request = fetch("https://www.google.com", {
method: "GET",
body: null,
mode: 'no-cors'
})
request.then( res => { return res.json() })
.catch(e => {
if (e instanceof SyntaxError){ console.log('Google OK')}
else if (e instanceof TypeError) { console.log('Google Failed')}
else {console.log('Unknown Error:' + e)}
});

如果请求成功,控制台输出 Google OK,如果失败,输出 Google Failed

这段代码可以在浏览器里面跑,也可以使用 Node.js 运行