现在有这么一个需求:后端返回 list 给你,id 是一个 20 位数的 int 类型,在 js 里叫做 number 类型,你需要用 js 变量存储该数据,在编辑数据时候传入该 id,并且后端强校验必须传入数字类型。
坑 1:
首先如果你不做任何处理直接去 getlist,比如如下代码:
1 | function getList() { |
这是因为 js 的 number 类型有个最大值(安全值),只能存储 2^53 内的数据,这个值是 9007199254740992,而我们的 12312312312312312312 很明显是大于这个安全值的。
那么该怎么办呢?可以使用 json-bigint 库(后面或许会考虑自己实现这么一个函数,先不纠结这个),这样拿到的数据就是字符串”12312312312312312312”,这样起码你能用变量存储这么一串数据了,虽然他目前变成了字符串:
1 | const list = [{ id: '12312312312312312312' }] |
至此,我们只是做到了 拿到数据并且正确存储数据,接下来是如何正确把该数据用 number 类型通过后端接口强校验,这里就涉及到坑 2 了:
我最开始的需求说了,后端 tm 强校验,必须传 number,不能传 string 的 id,你该怎么办?
我们先来说 fetch 请求,一般来说,你传的 body 其实可以是一个 typeof object 类型,而不是一个序列化后的 string 类型:
1 | // 直接传一个js对象 typeof 为object类型,是允许的: |
回到需求,我们的 id 是 12312312312312312312,假设我们这样传:
1 | fetch(url, { |
嘿嘿,你是不是以为这样不行,这样失去精度之类的…..其实上面是可以的…..但是,但是,但是!上面的代码是写死的数据,并不是把这个 id 先赋值给了 js 变量,再传入该变量。这里是直接写死的。对于 fetch 来说,该请求的 body 最终会解析成一个序列化 json 字符串,所以如果你在代码里面写死一个很大的死数据,其实是没问题的。
但下面这种方式,就会失去精度,导致传入的 id 不对:
1 | const id = 12312312312312312312 |
可以发现,你最终传入了一个错误的 id 给接口,这会导致你这次的操作失败。
那么该如何做,才能保证可以通过后端接口的强校验呢?(事实上,我个人认为这种长度的 id 本就不应该用 int,其次如果你后端真的用了 int,也应该允许前端传入字符串,不该做强校验。)
答案是转成 json 序列化再传。假设你已经用 json-bigint 库成功存储了这条数据:
1 | const item = { |
此时我们肯定不能直接用 JSON.stringify(item)传给后端,这样是没办法通过他的校验的。
此时序列化后的 json 字符串是:
1 | const str_item = '{"id":"12312312312312312312"}' |
而我们要做的最关键的一步,就是把这个 str_item 变成:
1 | const str_item2 = '{"id":12312312312312312312}' |
发现区别了吗?value 里面的引号没有了,意味着从 string 变成 number 的。
但是这里不能用 JSON.parse 转对象后再 Number(id),因为这个操作又会导致失去精度,所以我们只能全程操作字符串。
而这里的实现方式就各不相同了,比如你可以写个满足你规则的正则去替换掉里面的引号。
如果是复杂对象数组,我的建议是每层单独处理,比如该对象数组:
1 | // 此时的list已经是后端返回后 用json-bigint 库转成字符串了的: |
那么 str_list 应该是:’[{“pid”:”123123123123123123121”,”children”:[{“cid”:”123123123123123123122”},{“cid”:”123123123123123123123”}]}]’
我们要做的是把每个 pid 和 children 里面的 cid 的 value 的引号去掉,那么可以这么写:
1 | str_list |
此时 str_list 就变成了:’[{“pid”:123123123123123123121,”children”:[{“cid”:123123123123123123122},{“cid”:123123123123123123123}]}]’
这时候的数据是不能 JSON.parse 的,会失去精度,但是你可以直接把这段字符串传给接口,后端那边拿到你这段字符串,parse 后是能正确拿到该 id 的(拿不到的话 100%是后端技术问题)
所以总结一下,假如后端返回这条数据给你:
1 | function getList() { |
那么你需要用 json-bigint 库,拿到转成字符串且不丢失精度后的结果:
1 | const list = [ |
然后 JSON.stringify 变成:’[{“id”:”123123123123123123121”}]’
然后把该结果的 value 的字符串删除,变成:’[{“id”:123123123123123123121}]’
然后请求接口:
1 | const str_body = '[{"id":123123123123123123121}]' |
大功告成。