使用JavaScript的fetch重新读取响应体

40

fetch()返回一个Promise对象,如果成功则解析为一个Response对象。一个非常常见的操作是立即调用Response.json()将响应主体转换为JSON对象。

如果响应主体不是有效的JSON,则Response.json() Promise会失败并显示错误消息,消息大致如下:

在JSON中的位置0处,X的标记非预期

这对于诊断问题来说并不太有帮助。理想情况下,我想能够查看服务器内容(通常是错误消息)。

然而,在Chrome浏览器中似乎只能一次读取Response.body流(至少在Chrome中是这样)。 (甚至有一个只读的Response.bodyUsed标志。)当Response.json()尝试将主体转换为JSON时已经发生了这种情况,因此在JSON解析失败的情况下主体似乎永远丢失了。

除了手动读取原始fetch Promise解析后的响应,并将其转换为JSON之外,是否有其他方法可以恢复原始的响应主体?


5
如果返回的数据不是有效的 JSON 格式,你可以使用 response.text() 方法来读取数据。你也可以克隆一个响应对象,但最好的方法是始终返回 JSON 数据,即使是错误信息也应该以 JSON 的格式返回。 - adeneo
4个回答

48

使用 Response.clone() 来克隆 Response 对象

let clone = response.clone();

或者使用 Response.body.getReader(),它返回一个可读的ReadableStream,以流的形式读取Response,再使用TextDecoder()Uint8Array数据流转换为文本。


@CraigWalker 请参见如何在网络流中追加DOM元素? - guest271314
1
clone() 看起来是个不错的选择,但如果你已经调用了 .json(),它就会失败:"TypeError: Failed to execute 'clone' on 'Response': Response body is already used"。因此,resp.text() 后跟手动 json 解析是一个选项,但 Jaromanda 建议在调用 json() 之前先使用 clone()! - xander
1
@xander 是的,在读取 body 之前需要调用 .clone()。这就是为什么 Answer 中的 javascriptresponse.clone()。你看过规范 https://fetch.spec.whatwg.org/#body-mixin、https://fetch.spec.whatwg.org/#bodies、https://fetch.spec.whatwg.org/#dom-response-clone 吗? - guest271314
3
我遇到了 Failed to execute 'clone' on 'Response': Response body is already used 的错误。 - Pacerier
@Pacerier 如果 Response.body 已经在代码中使用过了,你能否创建一个 jsfiddle https://jsfiddle.net 或 plnkr https://plnkr.co 来演示一下? - guest271314
1
感谢 @guest271314 的回答,这里有一个可行的示例,如果需要更多帮助 https://codesandbox.io/s/parcel-sandbox-5sy2h - Mohamed

12

我不得不处理一个API,有时会出错返回JSON响应 - 在返回response.json()之前,我复制了响应对象。使用catch块,我可以确定错误是否为SyntaxError,并继续使用响应克隆的文本结果来修复错误。

就像这样:

var brokenJson = function (url) {
    var responseCopy;
    return fetch(url)
    .then(function (response) {
        responseCopy = response.clone();
        return response.json();
    }).catch(function (err) {
        if (err instanceof SyntaxError) {
            return responseCopy.text()
            .then(function(data) {
                return fixJson(data);
            });
        }
        else {
            throw err;
        }
    }).then(function (json) {
        // do things
    });
};

fixJson 是一个修复接收到数据的函数 - 在我的情况中,当它是破碎的 JSON 时,总是以相同的方式破碎 - 我想它有一个额外的前导 { 或尾随 } - 无法回忆起

重新阅读问题,你更可能想要将错误记录到控制台而不是修复 JSON - 简单重写:

var brokenJson = function (url) {
    var responseCopy;
    return fetch(url)
    .then(function (response) {
        responseCopy = response.clone();
        return response.json();
    }).catch(function (err) {
        if (err instanceof SyntaxError) {
            return responseCopy.text()
            .then(function(text) {
                console.error(text);
                throw err;
            });
        }
        else {
            throw err;
        }
    }).then(function (json) {
        // do things
    });
};

0

我使用了JSON.parse(response.resp.data),因为某种原因克隆(clone)无法正常工作。


0
将 response.json() 赋值给一个变量并返回它对我有用。clone() 再次说它被锁定了。
fetch("http://localhost:3000/watchlist")
    .then(response => {
      var res = response.json();
      return res;
    })
    .then(data => {
      console.log(data);
      this.setState({ data });
    });

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