数据库bigInt,后端Long类型定义的ID,导致前端与后台不一致
项目开发中前后端数据交互常会使用id作为主键索引,通常id数值都不大,使用number类型就可以表示处理,但对于一些分布式id或其他情况,后台数据库使用雪花ID,数据库使用bigInt类型存储,比如:
33978617558956897这个id长度17位,封装成对象传到前端后变成了:
33978617558956896起初在后端来排除,发现后端没有问题,那么问题只能是在前端,当后端返回了这样一个id数值的数据,可以看到此数值已经超过了JS的最大处理数,丢失了精度,前端此时拿到的id值是错误的。
出现的原因:
后端: 生成的是18位的纯数字,javaLong类型可以接收。
前端:js中数字类型最长为17位,它能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值。
Math.pow(2, 53) // 9007199254740992
9007199254740992 // 9007199254740992
9007199254740993 // 9007199254740992
Math.pow(2, 53) === Math.pow(2, 53) + 1上面代码中,超出 2 的 53 次方之后,一个数就不精确了。ES6 引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限。
Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1
// true
Number.MAX_SAFE_INTEGER === 9007199254740991
// true
Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER
// true
Number.MIN_SAFE_INTEGER === -9007199254740991
// true上面代码中,可以看到 JavaScript 能够精确表示的极限。
解决方案:
方案一:转为字符串方式
通过后端解决,把id转化为字符串类型返回,在字段上添加一个序列化格式注解即可:
@JsonSerialize(using = ToStringSerializer.class)
private Long studentId;这样获取到的id就是正确的id了,由后端来改是最方便快捷的。
方案二:bigint
JavaScript新增的基础数据类型bigint就可以解决此类问题,将id转化为bigint类型,使用到json-bigint插件处理json数据中的这类数值。
安装json-bigint:
npm i json-bigint下面是使用它的一个简单示例。
const jsonStr = '{ "art_id": 1245953273786007552 }'
console.log(JSON.parse(jsonStr)) // 1245953273786007600
// JSON.stringify()
// JSONBig 可以处理数据中超出 JavaScript 安全整数范围的问题
console.log(JSONBig.parse(jsonStr)) // 把 JSON 格式的字符串转为 JavaScript 对象
// 使用的时候需要把 BigNumber 类型的数据转为字符串来使用
console.log(JSONBig.parse(jsonStr).art_id.toString()) // 1245953273786007552
console.log(JSON.stringify(JSONBig.parse(jsonStr)))
console.log(JSONBig.stringify(JSONBig.parse(jsonStr))) // 把 JavaScript 对象 转为 JSON 格式的字符串转json-bigint 会把超出 JS 安全整数范围的数字转为一个 BigNumber 类型的对象,对象数据是它内部的一个算法处理之后的,我们要做的就是在使用的时候转为字符串来使用。
请求中使用:
通过 Axios 请求得到的数据都是 Axios 处理(JSON.parse)之后的,我们应该在 Axios 执行处理之前手动使用 json-bigint 来解析处理。Axios 提供了自定义处理原始后端返回数据的 api:transformResponse 。
import axios from 'axios'
// 遇到问题:后端返回的超大数字无法在JS中精确表示
// 解决方案:借助 json-bigint 将超大数字转成字符串即可
import bigint from 'json-bigint'
const JSONBig = bigint({"storeAsString": true}) // 通过该配置,让超大数字转为字符串
const request = axios.create({
baseURL: 'http://ttapi.research.itcast.cn/', // 接口基础路径
// transformResponse 允许自定义原始的响应数据(字符串)
transformResponse: [function (data) {
try {
// 如果转换成功则返回转换的数据结果
return JSONBig.parse(data)
} catch (err) {
// 如果转换失败,则包装为统一数据格式并返回
return {
data
}
}
}]
})
export default request
其他地方不需要改动,这个时候前后端数据交互时id参数传输的时候会自动转化为字符串类型传输{id: "
33978617558956897 "}。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!