后端id类型为long类型时,返回给前端浏览器四舍五入,导致id精度缺失问题

背景

今天在代码里,掉了别人写的接口,有个id的字段是long类型的,我这边加点参数返回给前端,然后前端根据id修改,结果修改的数据记录有,但是没起作用,后来发现根据他传给我的id在后台数据库查不到数据,然后发现,在浏览器控制台中后端响应的数据和前端预览的数据不一致,如下图

  • 预览数据(前端最后收到的)
    在这里插入图片描述
  • 响应数据(后端返回的)
    在这里插入图片描述

问题

我遇到的是一共19位,但后面两位会四舍五入然后变成00

原因

java中long值的范围,比JavaScript中的Number取值范围大,所以数据超出JavaScript中的Number取值范围时,精度会出现问题

JavaLong的取值范围为-92233720368547758089223372036854775807(即-2^64“ 到”2^64-1
JavaScript中的Number取值范围为-90071992547409929007199254740991 (即-2^532^53-1

解决方法

后端把long类型的,改成String字符串返回,序列化返回的时候,变成String类型

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
<think>我们面对的问题是:前端需要处理后端返回的包含long类型id的数组,由于JavaScript的Number类型只能安全表示$-2^{53}$到$2^{53}$之间的整数,而long类型(64位)可能超出这个范围,导致精度丢失。因此,我们需要在前端进行特殊处理。 根据引用[1]和[2]中的描述,问题在于后端返回Long类型数字在JSON中被解析为JavaScript的Number类型,如果数字超过$2^{53}$(即9007199254740992),则会出现精度丢失。 解决方案: 1. **最佳方案:协调后端long类型id以字符串形式返回**(如引用[1][^1]和引用[2][^2]所建议)。这样前端接收到的就是字符串,不会丢失精度。 2. **如果后端无法修改,前端需要自行处理**:在解析JSON,将大整数转换为字符串或BigInt。 针对用户的具体需求:处理包含long类型id的数组,我们可以采用以下方法: ### 方法1:使用json-bigint库处理整个响应 安装`json-bigint`库: ```bash npm install json-bigint ``` 在请求响应处理中使用: ```javascript import JSONBig from 'json-bigint'; // 假设我们通过axios等库获取响应,响应文本为responseText const responseText = '{"ids": [1405916999732707330, 1405916999732707331]}'; const data = JSONBig.parse(responseText, { storeAsString: true }); console.log(data.ids); // 此数组中的每个元素都是字符串,例如 ["1405916999732707330", "1405916999732707331"] ``` - 使用`storeAsString: true`选项,将大整数转换为字符串,避免精度丢失。 ### 方法2:使用axios的transformResponse(如果使用axios) 在axios请求中,我们可以使用`transformResponse`在解析之前处理响应字符串: ```javascript import axios from 'axios'; import JSONBig from 'json-bigint'; const api = axios.create({ baseURL: 'https://example.com', transformResponse: [data => { try { // 使用json-bigint解析 return JSONBig.parse(data, { storeAsString: true }); } catch (e) { return data; } }] }); // 使用该api实例发请求,返回的数据中long类型id会被转为字符串 ``` ### 方法3:手动处理(不推荐,仅作备选) 如果不想引入库,可以手动处理JSON字符串,将大整数替换为字符串。但是这种方法需要知道哪些字段是long类型,或者通过正则匹配大数字(风险:可能误匹配)。 例如,假设我们只处理id字段(包括数组中的id): ```javascript const responseText = '{"ids": [1405916999732707330, 1405916999732707331]}'; // 使用正则匹配所有大于等于15位的数字(因为2^53是16位,所以15位以上就可能有问题,但具体看实际情况) const safeResponseText = responseText.replace(/(\:\s*)(\d{15,})(\,|\}|\])/g, '$1"$2"$3'); const data = JSON.parse(safeResponseText); console.log(data.ids); // 数组中的每个元素是字符串 ``` 注意:这个正则表达式可能不够健壮,需要根据实际JSON结构调整。 ### 方法4:使用BigInt(现代浏览器) 如果目标环境支持BigInt,并且你希望保留数字的大整数特性(而不是字符串),可以使用: ```javascript const responseText = '{"ids": [1405916999732707330, 1405916999732707331]}'; const data = JSON.parse(responseText, (key, value) => { // 判断数字是否超过安全范围 if (typeof value === 'number' && value > 9007199254740991) { return BigInt(value).toString(); // 转为字符串,或者直接返回BigInt对象 } return value; }); ``` 但是注意,这种方法在解析,如果数字已经被解析为Number(并且已经丢失精度),那么再转为BigInt就晚了。所以必须在解析JSON字符串进行转换,而JSON.parse的第二个参数reviver是在解析过程中转换,但此如果数字已经被解析成Number,那么可能已经丢失精度了。因此,这种方法不可靠! 正确做法是:在解析之前,确保数字不被解析为Number。因此,我们需要在解析字符串阶段就标记出大数字,所以还是需要像json-bigint那样处理。 ### 总结 - **推荐使用`json-bigint`库**,它可以安全地将大整数转为字符串(或BigInt对象),避免精度丢失。 - 对于数组中的每个id,我们将其转为字符串,这样在JavaScript中就可以安全使用(如展示、作为字符串传递等)。 - 如果后续需要运算,可以转为BigInt(注意兼容性)或者使用大整数运算库(如bignumber.js)。 ### 示例代码(使用json-bigint处理数组) ```javascript import JSONBig from 'json-bigint'; // 假设后端返回的JSON字符串 const responseText = '{"ids": [1405916999732707330, 1405916999732707331]}'; // 使用json-bigint解析,并设置storeAsString为true,将大整数转为字符串 const data = JSONBig({ storeAsString: true }).parse(responseText); // 此data.ids是一个字符串数组 console.log(data.ids); // ["1405916999732707330", "1405916999732707331"] // 如果需要使用BigInt,可以转换(注意:BigInt在运算使用,不能直接用于普通数字运算) const bigIntIds = data.ids.map(id => BigInt(id)); console.log(bigIntIds[0] + BigInt(1)); // 1405916999732707331n ``` ### 注意事项 1. 如果使用BigInt,注意它不能和普通Number混合运算,需要先转换。 2. 在展示,通常使用字符串即可,避免使用BigInt,因为一些API可能不支持BigInt(如JSON.stringify需要额外处理)。 通过以上方法,前端可以安全处理后端返回long类型id数组,确保精度丢失。 [^1]: Java后端返回Long类型数据与前端展示不一致(精度丢失) [^2]: 后端Long型数据传给前端精度丢失问题 分布式id 解决方案</think>### 前端处理 long 类型 ID 数组的精度问题解决方案 当后端返回包含 long 类型 ID 的数组(如 `[1405916999732707330, 1405916999732707331]`),由于 JavaScript 的数值精度限制(仅支持 ≤ $2^{53}$ 的整数),会导致精度丢失(如 `1405916999732707330` 显示为 `1405916999732707300`)。以下是系统解决方案: --- ### 解决方案(按推荐顺序) #### 1. **最佳方案:使用 json-bigint 库(推荐)** ```javascript import JSONBig from 'json-bigint'; // 处理响应数据(自动将大整数转为字符串) const parseResponse = (response) => { return JSONBig({ storeAsString: true }).parse(response); }; // 示例:处理包含 long 数组的响应 const response = '{"ids": [1405916999732707330, 1405916999732707331]}'; const data = parseResponse(response); console.log(data.ids); // 输出: ["1405916999732707330", "1405916999732707331"] (字符串数组) ``` **优点**: - 自动处理所有大整数 - 支持 `BigInt` 或字符串两种格式(通过 `storeAsString` 配置) - 兼容 axios 等请求库(可在拦截器中集成) --- #### 2. **替代方案:自定义 JSON 解析** ```javascript const parseLongArray = (jsonString) => { return JSON.parse(jsonString, (key, value) => { // 识别 long 类型字段(根据实际字段名调整) if (key === 'ids' && Array.isArray(value)) { return value.map(id => id.toString()); } return value; }); }; // 使用示例 const response = '{"ids": [1405916999732707330, 1405916999732707331]}'; const data = parseLongArray(response); console.log(data.ids[0]); // "1405916999732707330"(字符串) ``` --- #### 3. **正则预处理(应急方案)** ```javascript const safeParse = (json) => { // 匹配 15 位以上数字(根据实际 ID 长度调整) const regex = /:\s*(\d{15,})/g; const safeJSON = json.replace(regex, ': "$1"'); return JSON.parse(safeJSON); }; // 使用示例 const response = '{"ids": [1405916999732707330, 1405916999732707331]}'; const data = safeParse(response); console.log(data.ids); // ["1405916999732707330", ...] ``` > ⚠️ 注意:此方法可能误转换非 ID 字段,需谨慎使用[^3]。 --- ### 最佳实践建议 1. **ID 始终作为字符串处理**: ```javascript // 所有 ID 相关操作使用字符串 const id = "1405916999732707330"; fetch(`/api/item/${id}`); // 直接使用字符串 ``` 2. **在框架中的集成**(以 React 为例): ```javascript // axios 拦截器配置(src/utils/request.js) import axios from 'axios'; import JSONBig from 'json-bigint'; const service = axios.create({ transformResponse: [data => { return data ? JSONBig({ storeAsString: true }).parse(data) : {}; }] }); ``` 3. **严格类型检查**: ```javascript // 使用 TypeScript 确保类型安全 interface Item { id: string; // 明确声明为字符串 name: string; } const items: Item[] = await fetchItems(); ``` --- ### 关键注意事项 1. **禁止使用 `toFixed()` 或数值运算** 如引用[3]所述:`1.365.toFixed(2)` 会导致精度问题,对大整数完全不可用[^3]。 2. **避免隐式类型转换** ```javascript // 错误示例(隐式转为 Number 导致精度丢失) const id = 1405916999732707330; // 正确做法(始终显式保持字符串) const id = "1405916999732707330"; ``` 3. **BigInt 的兼容性限制** 虽然现代浏览器支持 `BigInt`,但需注意: - 旧版浏览器(如 IE)不支持 - JSON 序列化需特殊处理(`JSON.stringify` 需自定义) - 运算需使用 `BigInt()` 包装(如 `BigInt(id) + 1n`) 通过以上方案,可确保 long 类型 ID 数组在前端处理的完整性,避免出现 `1405916999732707330 → 1405916999732707300` 的精度丢失问题[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值