阅读 441

【坑】后端返回long int导致的问题

问题的出现

其实场景很简单,前端向后端请求歌手列表数据,然后使用table来展示。当用户点击table中的某一行的时候,我们就根据这条记录的歌手id去请求该歌手下面的专辑列表。

问题的发现开始于,我正常发起请求,后端成功返回数据,只不过返回的列表数据为空。我把这个结果反馈给后端同事。他让我把这个歌手的id发给他去查一下。不查不知道,一查,他发现数据库中根本不存在这个id。我就想,那就奇怪了,这个id是你后端返回给我的啊。

于是,我到了chrome开发者工具的network面板preview tab上面去查看,我发现preview tab中显示后端返回的id就是25322104441816936,而我在请求参数中提交的也是25322104441816936。我拍着胸脯说:“前端没错啊,你返回什么给我,我就提交什么给你嘛”。

后端同事和我在一脸懵逼之后,我们决定比对一下他返回的数据和我拿到的数据之间的差异。注意,这个时候,我还是看preview tab的,因为response tab是未经过格式化的json序列串,一般我们都不会看那里。经过比对,我们发现,后端返回的id值(25322104441816935)跟“前端拿到的id值25322104441816936)”是不一致的。

感觉前后端都没有问题,我们之间又没有中间层,那问题出现在哪里呢?

问题的根源

二话不说,现在网上搜索一下:“前端拿到数据与后端返回数据不一致”,看到以下的搜索结果:

直觉告诉我,第二个搜索结果比较靠近遇到的问题。于是点进去看了看,发现里面提到了preview tabresponse tab所显示的值是不同的。我马上去先查看了response tab

再点开preview tab,它的显示是这样的:

狗日的,这事还真的存在。看来,后端没有错,是前端的问题。之前我们怀疑的“这个问题是由于id值是java的long int类型,到了前端这边因为解析而导致了精度丢失而引起的”也是对的。

与此同时,我也更新了并强化了这么一个认知:“chrome开发者工具的network面板中的preview tab是对后端的json数据进行解析后的显示,它也会出错,不要绝对信任。想要确定后端返回数据的原始值,还是要看未经过解析的response tab

到这里,问题的根源算是找到了。那就是:“因为后端返回的id值超出了js中最大的安全整数值(Number.MAX_SAFE_INTEGER),导致了chrome开发者工具的preview tab和我所用的axios请求类库在解析json序列串的时候出现了精读丢失,从而引起了前后端数据不一致。”

解决方案

解决方案有二:

  • 后端侧解决。long int类型的数据一律返回字符串给前端。那么前端默认使用JSON.parse()去解析的时候得到的也是字符串。
  • 前端侧解决。手动解析后端返回的json序列串,而不使用原生的JSON.parse来一刀切解析。

有时候,依靠后端侧来解决问题不在我们控制的范围内。我们要学会自力更生。这里具体问题具体分析。因为我使用的是axios来作为我的异步请求类库,所以我着重来谈谈在这个背景下的解决方案。

也就是说,问题转换为“如何在axios中解析big int?”。拿着这个问题在网上一查,相信我们很快就找到答案。这里,我记录一个比较简单的答案:通过添加配置,使用第三方类库json-bigint来解析big int。

废话不多说,上代码:

import axios from 'axios'
import JSONbig from "json-bigint";

// 引入【json-bigint】包是因为后端返回的id值是long int类型,并且超出了js的最大安全整数的值(Number.MAX_SAFT_INTEGER)
const JSONbigToString = JSONbig({ storeAsString: true })


const instance = axios.create({
  baseURL: process.env.REACT_APP_API_BASE,
  timeout: 20000,
  transformResponse: [function (data) {
    return JSONbigToString.parse(data)
  }]
})
复制代码

鉴于我们不需要对id进行四则运算,所以,我们可以放心地将它解析为字符串。这就是为什么加入“{ storeAsString: true }”配置的原因。

题外话,json-bigint这个包正如它的名字那样,是专门用于处理待解析的json字符串包含big int数据的情况。它的readme是这么介绍的:

JSON.parse/stringify with bigints support. Based on Douglas Crockford JSON.js package and bignumber.js library.

鉴于它是基于Douglas Crockford大佬的JSON.js和大名鼎鼎的bignumber.js来实现的,我们大可放心使用。三者也可以看作是javascript的big int相关生态中的标准库。

以上是本次的踩坑之旅的完整记录。记之,以此为鉴。

参考资料