当前位置:网站首页>js Fetch返回数据res.json()报错问题
js Fetch返回数据res.json()报错问题
2022-08-03 03:09:00 【[虚幻私塾】】
优质资源分享
学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
🧡 Python实战微信订餐小程序 🧡 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
Python量化交易实战 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
前言
一直以来在简单的场景中经常使用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封装的应该都是很好用的
///
/// 模拟处理数据
///
///
[HttpPost("handle-data")]
public ActionResult HandleData(string mockData)
{
// 业务逻辑
// ......
// 处理完成,直接告诉浏览器我Ok了, 状态200
return Ok();
}
此时前端在处理完成后, 控制台面板中 Network 中也显示post成功返回200了, 但是在 Console 中却有一条红色报错信息
请求失败了,详细信息:{}
看到报错位置竟然是post中的catch, 可明明在Network中看到返回200了啊, 稍作镇静之后就意识到应该就是返回时数据处理报错了, 在resolve(res)
上面打印也没走这个逻辑, 那就是 上一层 .then(res=> res.json()
有问题。
// 将.then(res=> res.json()) 替换成下面
.then(res => {
console.log(res.json());
return 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://blog.csdn.net/cnwwm
联系:[email protected] 微信:w_wmin
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。如有问题或建议,请多多赐教,非常感谢。
边栏推荐
- 9 椭圆曲线密码体制
- Jincang Database OCCI Migration Guide (5. Program Development Example)
- C语言——-动态内存开辟与管理(malloc,free,calloc,realloc)+柔性数组
- 详细讲解一下JVM的内存模型与实现?
- 实现统一账号登录,sonarqube集成ldap
- 【数据分析】基于MATLAB实现SVDD决策边界可视化
- mysql8默认密码丢失,如何更改密码详细步骤??
- MySQL-多表查询
- PyTorch安装——安装PyTorch前在conda搭建虚拟环境的报错
- 金仓数据库 Pro*C 迁移指南( 4. KingbaseES 的 Pro*C 迁移指南)
猜你喜欢
Best Practices for Migration from Jincang Database from MySQL to KingbaseES (3. MySQL Database Migration Practice)
金仓数据库 Pro*C 迁移指南( 4. KingbaseES 的 Pro*C 迁移指南)
ClickHouse—高级
IPv4编址;A类、B类、C类、D类、E类IP地址(IP地址;网络地址和主机地址;子网掩码;网关;广播地址;)
C语言入门--指针
如何画一张架构图(内含知识图谱)
Kotlin 乘法、我怎么越乘越小?
网易数帆陈谔:云原生“牵手”低代码,加速企业数字化转型
SPI机制是什么?
服务器在线测速系统源码
随机推荐
ClickHouse—入门
谷粒商城一些疑问总结
PyTorch安装——安装PyTorch前在conda搭建虚拟环境的报错
金仓数据库 MySQL 至 KingbaseES 迁移最佳实践(3. MySQL 数据库移植实战)
sql问题,如何能做到先声明表的名称,例如product202201,表示2022年一月份的货物表,再在声明过的表中查找,下面的代码运行时有错误显示找不到表table_name,请问改如何进行修改
金仓数据库 OCCI 迁移指南(5. 程序开发示例)
Sentinel vs Hystrix 限流对比,到底怎么选?
C语言——-动态内存开辟与管理(malloc,free,calloc,realloc)+柔性数组
金仓数据库 Pro*C 迁移指南( 5. 程序开发示例)
任意版本JLink驱动官方下载指引
【GraphQL】使用Hot Chocolate和.NET 6构建GraphQL应用
【剑指offer】——16.数值的整数次方
ROS2自学笔记:机器视觉基础
ROS通信模块:秒懂话题通信
C语言实验十三 指针(三)
什么是数据标注? 数据标注公司主要做什么?
15【背景 渐变色】
leetcode:163 缺失的区间
IPv4编址;A类、B类、C类、D类、E类IP地址(IP地址;网络地址和主机地址;子网掩码;网关;广播地址;)
【剑指offer】——股票的最大利润