response.json() throws "TypeError: Failed to fetch"

response.json() throws "TypeError: Failed to fetch"

我使用 fetch for getting some resources from the server. I can see from logs that from time to time conversion to JSON"TypeError: Failed to fetch" 上失败,这非常有趣,因为这种类型的错误应该只在请求失败时发生。

我使用的简化代码:

const response = await fetch('https://something.com/api')

try {
   await response.json() // -> throws TypeError: Failed to fetch in chrome/safari and throws AbortError in Firefox
} catch(error) {
   console.log('error happened', error);
}

我真的找不到可能发生的情况。我测试了可能的情况,但都在第一行代码上失败了,即 fetch('https:/something.com/api')。我不知道这什么时候会发生。我还应该提到它发生在像 chrome 99 这样的现代浏览器中。所以它绝对不是像 Internet Explorer 那样的东西。

我找到了这个有用的示例,它显示当您卸载文档时请求被取消。显然,取消发生在 fetch 行,但我决定在文档为 unloaded/hidden 时停止记录这些错误。即使没有记录这些案例,我也可以看到它也发生在可见文档上。

https://github.com/shuding/request-cancellation-test

测试案例:

  1. 网络错误 - 用户已断开与互联网的连接
  2. CORS - 缺少 CORS headers

显然,这并不能证明什么,但评论中的人认为我在实现中做错了什么,并且当我说转换为 json 时确实发生了时不相信我。这是我在发现错误时能够记录的信息。大多数属性来自 Response object.

这是使用 chrome 100 从访问者那里捕获的日志。Firefox 不会抛出 TypeError,而是抛出 AbortError,但在转换为 json 时也会发生这种情况。

你在这行的错误:

const response = await fetch('https://something.com/api')

我删除了await response.json()进行测试,错误依然存在

请查看 DevTool 中的控制台选项卡,确保您使用此设置

这些是 2 个示例错误 Failed to fetch:

(async () => {

try {
    const response = await fetch('http://my-api.com/example')
    await response.json() 
} catch (e) {
    console.log('e')
    console.log(e)
}
})()

这看起来像是在收到响应 HTTP headers 之后发生的网络错误。在这种情况下,fetch 方法 returns 成功,但随后尝试访问响应 body 可能会失败。

我设法用这个简单的服务器触发了这种错误:

#!/usr/bin/env python3
import http.server
import time

class Handler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/':
            self.wfile.write(b'HTTP/1.0 200 OK\r\n')
            self.wfile.write(b'Content-type: text/html\r\n')
            self.wfile.write(b'\r\n')
            self.wfile.write(b"""\
<script type="module">
    const f = await fetch("/j");
    try {
        await f.json();
    } catch (e) {
        alert(e);
    }
</script>
""")
            return

        self.wfile.write(b'HTTP/1.0 200 OK\r\n')
        self.wfile.write(b'Content-encoding: gzip\r\n')  # bogus
        self.wfile.write(b'Content-type: application/json\r\n')
        self.wfile.write(b'\r\n')
        self.wfile.write(b'[]')


server = http.server.HTTPServer(('127.0.0.1', 31337), Handler)
server.serve_forever()

它在提供 JSON 响应时引入了故意的框架错误; headers 表示使用了 gzip 压缩,但实际上没有使用压缩。当我在 Chromium 100.0.4896.127 中打开 http://127.0.0.1:31337/ 时,它会显示以下警告:

TypeError: Failed to fetch

Firefox ESR 91.8.0 显示稍微有用的:

TypeError: Decoding failed.

上面展示的特定帧错误是人为设计的,我怀疑提问者所经历的正是这种错误。但它出现在响应中间的事实 body 可能是所描述问题的核心。

可以通过修改服务器来触发问题中的特定错误类型对:

        self.wfile.write(b'HTTP/1.1 200 OK\r\n')
        self.wfile.write(b'Content-type: application/json\r\n')
        # sic: larger than the actual body length
        self.wfile.write(b'Content-length: 2\r\n')
        self.wfile.write(b'\r\n')
        self.wfile.write(b'[')

这会显示相同的警报:

TypeError: Failed to fetch

在 Chromium(相同版本)中,并且

AbortError: The operation was aborted.

在 Firefox 中(也一样)。

因此,一个可能的原因是在接收 body 的过程中断开连接。浏览器的错误在这里没有特别详细(有时是彻头彻尾的误导),所以我们只能猜测,但这似乎是最好的猜测。