本文转载自微信公众号“吴佩轩”,作者吴佩轩。转载本文请联系吴佩萱公众号。看的时候,发现前后端有返回超大整数的规定。和前端沟通后,才知道这是JavaScript的一个坑。我们重现这个错误:创建一个SpringBoot工程,然后新建一个接口,可以返回一个DbScript对象,其中id为mybatis-plus的IdWorker。由getId(基于Snowflake算法)生成的19位长值。@RestController@RequestMapping("/dbScript")publicclassDbScriptController{Loggerlogger=LoggerFactory.getLogger(DbScriptController.class);@RequestMapping("/info")publicDbScriptgetDbScript(){DbScriptdbScript=newDbScript();//给一个大整数长脚本idlongid=IdWorker.getId();dbScript.setId(id);logger.info("id:{}",id);returndbScript;}}然后启动服务,在浏览器上访问界面,结果如下:从日志中可以看出,后端传给前端的id是1304270071757017088,而前端获取到的id是1304270071757017000,其中存在精度损失。为什么会这样?通过开发手册我们可以知道,如果返回值超过2的53次方,就会被转成JSNumber,此时有些值可能会丢失精度。解决方案那么如果遇到这种情况,该如何解决呢?不要惊慌,可以采取以下方法:如果这个对象只是在这个方法中使用,可以直接将属性从Long类型改为String类型。如果这个对象用的比较多,可以在序列化的时候把Long类型转成String类型。还可以新增一个String类型的属性,专门用来在前后端之间传递这么大的整数。第一种方法第一种方法比较简单,直接改Longid;toStringid;,只适用于这个对象,只在这个方法中使用,比较受限。方法二方法二可以给属性添加注解。如果使用Jackson,则可以添加@JsonFormat(shape=JsonFormat.Shape.STRING)或@JsonSerialize(using=ToStringSerializer.class)注解。如果需要修改的情况比较多,一个一个添加有点麻烦,请问还有什么好的方法吗?如果你使用的是Jackson,它有一个配置参数WRITE_NUMBERS_AS_STRINGS,可以强制将所有数字转换成字符串输出。使用方法很简单,只需要配置参数:spring.jackson.generator.write_numbers_as_strings=true,这种方法的好处是简单易用,不需要调整代码;缺点是粒度太大,所有的数字都转换成字符串输出,包括时间戳格式的时间输出。那么有没有其他方法可以只将Long类型转换成String类型呢?Jackson提供了这种支持,可以自定义ObjectMapper。具体代码如下:publicclassJacksonConfiguration{@BeanpublicJackson2ObjectMapperBuilderCustomizerjackson2ObjectMapperBuilderCustomizer(){returnjacksonObjectMapperBuilder->jacksonObjectMapperBuilder.serializerByType(Long.class,ToStringSerializer.instance).serializerByType(Long.TYPE,ToStringSerializer.instance);}}通过定义Jackson2ObjectMapperBuilderJacksonObjectMapperBuilder2Customizer,自定义对象,自定义Long数据,使用ToStringSerializer进行序列化。第三种方法第三种方法需要多一个属性,比如用StringdbScripId来代替之前的id。综上所述,本文针对?中需要使用超大整数的场景提出了几种解决方案。服务器始终使用String字符串类型返回,禁止使用Long类型。您可以根据自己的需要选择方法。其他解决方案也欢迎留言讨论。
