js Fetch返回数据res.json()报错问题

前言

一直以来在简单的场景中经常使用fetch代替第三方请求库, fetch是JavaScript的原生函数, 简单、高效、快速、稳定、可定制等等诸多优点。一直也是用着很是舒服,直到有一天它竟然报错了。

什么是Fetch?

官方: Fetch API 提供了一个 JavaScript 接口,用于访问和操纵 HTTP 管道的一些具体部分,例如请求和响应。它还提供了一个全局 fetch() 方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。

还原现场

因为业务需求简单,这里只封装了get和post方法, 并且后端数据都是已默认的json格式返回

const http = {   apiBaseUrl: config.apiBaseUrl,   get: function (url) {     return new Promise((resolve, reject) => {       fetch(this.apiBaseUrl + url, {         method: 'GET',         headers: {           'Content-Type': 'application/json',           'Accept': 'application/json',         }       }).then(res => res.json()).then(res => {         resolve(res);       }).catch(e => {         console.error("请求失败了,详细信息:" + JSON.stringify(e));         reject(e);       });     })   },   post: function (url, body) {     return new Promise((resolve, reject) => {       fetch(this.apiBaseUrl + url, {         method: "POST",         headers: {           'Content-Type': 'application/json',           'Accept': 'application/json',         }, body: JSON.stringify(body)       }).then(res => res.json()).then(res => {         resolve(res);       }).catch(e => {         console.error("请求失败了,详细信息:" + JSON.stringify(e));         reject(e);       })     })   } } 

这样的封装貌似也没什么问题 😃。

后端没有做统一的返回数据格式约定, 直接使用.net中的ActionResult结构返回, .net封装的应该都是很好用的 😃

/// <summary> /// 模拟处理数据 /// </summary> /// <returns></returns> [HttpPost("handle-data")] public ActionResult HandleData(string mockData) {     // 业务逻辑     // ......          // 处理完成,直接告诉浏览器我Ok了, 状态200     return Ok(); } 

此时前端在处理完成后, 控制台面板中 Network 中也显示post成功返回200了, 但是在 Console 中却有一条红色报错信息

请求失败了,详细信息:{} 

js Fetch返回数据res.json()报错问题
看到报错位置竟然是post中的catch, 可明明在Network中看到返回200了啊, 稍作镇静之后就意识到应该就是返回时数据处理报错了, 在resolve(res)上面打印也没走这个逻辑, 那就是 上一层 .then(res=> res.json()有问题。

// 将.then(res=> res.json()) 替换成下面 .then(res => {    console.log(res.json());    return res.json(); }) 

这样写同样报错

js Fetch返回数据res.json()报错问题

可以看到时解析json报错了, 嗯, 因为我们就是没有返回任何数据, 解析自然会报错!
既然没有值Json解析报错, 那解决办法自然就得加一层判断了(也可以让后端必须返回一个Json, 简单粗暴, 哈哈! ) , 思路是先读取值然后判断是否为空.
但是打印res时

// 将.then(res=> res.json()) 替换成下面 .then(res => {    console.log(res);    let arrayBuffer = res.json();    let json = res.json();    return res.json(); }) 
body: ReadableStream          locked: true          [[Prototype]]: ReadableStream bodyUsed: true headers: Headers [[Prototype]]: Headers ok: true redirected: false status: 200 statusText: "OK" type: "cors" url: "'http://localhost:5069/api/handle-data" [[Prototype]]: Response 

而且还有一条报错信息

Uncaught (in promise) TypeError: Failed to execute 'json' on 'Response': body stream already read 

body stream already read说明流只能读取一次,
body是一个ReadableStream数据流,必须先读取流才能看到数据, 那就看一下是否还能转换成其他格式的数据.
查找MDN https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch#body

Body 类定义了以下方法(这些方法都被 Request 和 Response所实现)以获取 body 内容。这些方法都会返回一个被解析后的 Promise 对象和数据。

可知有5种数据格式,因为json和text可使用js原生方法JSON.parse/JSON.stringify相互转换, 那就直接选用.text()转成字符串判断即可.

// 将.then(res=> res.json()) 替换成下面 .then(res => {   let data = res.text();//转成字符串判断   return data.then(r => {   if (r.length === 0) return null;   else return JSON.parse(r);   }) }) 

验证结果正确, 一切又回到了正常。

简单封装fetch 获取json或空数据

let checkStatus = res => {   if (res.status >= 200 && res.status < 300) return res;   else {     let err = new Error(res.statusText);     err.response = res;     throw err;   } } let parseJson = res => {   let data = res.text();   return data.then(r => {     if (r.length === 0) return null;     else return JSON.parse(r);   }) } const http = {   apiBaseUrl: config.apiBaseUrl,   get: function (url) {     return new Promise((resolve, reject) => {       fetch(this.apiBaseUrl + url, {         method: 'GET',         headers: {           'Content-Type': 'application/json',           'Accept': 'application/json',         }       }).then(checkStatus).then(parseJson).then(res => {         resolve(res);       }).catch(e => {         console.error("请求失败了,详细信息:" + JSON.stringify(e));         reject(e);       });     })   },   post: function (url, body) {     return new Promise((resolve, reject) => {       fetch(this.apiBaseUrl + url, {         method: "POST",         headers: {           'Content-Type': 'application/json',           'Accept': 'application/json',         }, body: JSON.stringify(body)       }).then(checkStatus).then(parseJson).then(res => {         resolve(res);       }).catch(e => {         console.error("请求失败了,详细信息:" + JSON.stringify(e));         reject(e);       })     })   } } 

嗯 , 前端能处理的问题就不麻烦后端了 😃

总结

fetch返回的是数据流, 最终是什么格式数据需要我们自己判断使用, arrayBuffer/blob/formData/json/text 这些格式都有其使用场景, 前端er不要将思维只限定在了json哦。

作者:wwmin
出处:https://www.cnblogs.com/cnwwm
联系:wwei.min@163.com 微信:w_wmin
js Fetch返回数据res.json()报错问题
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。如有问题或建议,请多多赐教,非常感谢。

发表评论

相关文章