前言

一直以来在简单的场景中经常使用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 中却有一条红色报错信息

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



看到报错位置竟然是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://www.cnblogs.com/cnwwm

联系:wwei.min@163.com 微信:w_wmin



本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。如有问题或建议,请多多赐教,非常感谢。

js Fetch返回数据res.json()报错问题的更多相关文章

  1. Hibernate 连接数据库,数据库返回数据超过限制报错

    1.packet for query is too large 1024 >. you can change this value on the server mysql max_allowed ...

  2. 02 - Unit01:服务器返回数据的json处理+搭建项目环境

    服务器返回数据的json处理+搭建项目环境 服务器返回数据的json处理 springMVC JSP响应流程 请求 -->DispatcherServlet -->HandlerMappi ...

  3. 对Ajax返回的json数据做处理报错

    这个错误出现的原因是我再返回数据为json时,我页面的Ajax没有指定dataType: 'json'

  4. spring jpa 实体互相引用返回restful数据循环引用报错的问题

    spring jpa 实体互相引用返回restful数据循环引用报错的问题 Java实体里两个对象有关联关系,互相引用,比如,在一对多的关联关系里 Problem对象,引用了标签列表ProblemLa ...

  5. Springboot实体类转JSON报错Could not find acceptable representation & 设置访问项目根路径的默认欢迎页面

    =================实体类转JSON报错的解决办法============= 之前在springmvc的时候也报过这个错,原因以及springmvc中解决办法参考:https://www ...

  6. fasterxml.jackson 将对象转换为json报错处理

    最近在做查询的数据遇到如下报错: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found ...

  7. Newtonsoft.Json报错:未能加载文件或程序集"..."或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配

    Newtonsoft.Json报错:未能加载文件或程序集"..."或它的某一个依赖项.找到的程序集清单定义与程序集引用不匹配.   □ 背景分析 在帮助类库中使用了Newtonso ...

  8. golang解析json报错:invalid character '\x00' after top-level value

    golang解析json报错:invalid character '\x00' after top-level value 手动复制字符串:{"files":["c:/t ...

  9. vue.js 中使用(...)运算符报错的解决方法

    vue.js 中使用(...)运算符报错的解决方法 Syntax Error:Unexpected token(XX:X) }, computed:{ ...mapGetters([ 'pageSiz ...

随机推荐

  1. 167. Two Sum II - Input array is sorted - LeetCode

    Question 167. Two Sum II - Input array is sorted Solution 题目大意:和Two Sum一样,这里给出的数组是有序的 思路:target - nu ...

  2. Johnson 全源最短路

    学这个是为了支持在带负权值的图上跑 Dijkstra. 为了这个我们要考虑把负的权值搞正. 那么先把我们先人已经得到的结论摆出来.我们考虑先用 SPFA 对着一个满足三角形不等式的图跑一次最短路,具体 ...

  3. Kafka 消费者解析

    一.消费者相关概念 1.1 消费组&消费者 消费者: 消费者从订阅的主题消费消息,消费消息的偏移量保存在Kafka的名字是__consumer_offsets的主题中 消费者还可以将⾃⼰的偏移 ...

  4. 前端2CSS

    内容概要 form表单 网络请求方式 CSS简介 CSS查找标签之基本选择器(重要) CSS查找标签之组合选择器(重要) 属性选择器 分组与嵌套 伪类选择器 内容详情 form表单 "&qu ...

  5. ExtJS配置TabPanel可以拖拽Tab标签页

    1.环境说明 ExtJS版本:7.4.0.42 Sencha Cmd: v7.5.1.20 开发工具:WebStorm 2022.1.1 PS:如果是老版本的ExtJS,引入Ext.ux.TabReo ...

  6. powershell命令总结

    2021-07-21 初稿 ps命令采用动词-名词的方式命名,不区分大小写.默认当前文件夹为当前路径./.除去-match使用正则表达式匹配外,其他都使用*和?通配符. 速查 管道命令 前一个的输出作 ...

  7. sap 调用Http 服务

    REPORT ZMJ_GETAPI. DATA: LEN TYPE I, "发送报文长度 LEN_STRING TYPE STRING, URL TYPE STRING, "接口地 ...

  8. zabbix监控mysql主从同步

    获取主从复制sql线程和I/O线程状态是否为yes #!/bin/bash HOSTNAME="数据库IP" PORT="端口" USERNAME=" ...

  9. Flink1.13.1源码解析-Application on yarn(一)

    本篇文章讲述 Flink Application On Yarn 提交模式下,从命令提交到 AM 容器创建 1.脚本入口 flink run-application -t yarn-applicati ...

  10. Nginx开机自启

    编写service脚本: vim /usr/lib/systemd/system/nginx.service 将以下内容复制到nginx.service文件中 ps:我的nginx目录是/usr/lo ...