spring返回数据统一处理,返回数据序列化,前后端Long丢失精度

统一数据返回处理
  -大数字 转 字符串 (前端会丢失精度,所以转字符串)
  -null 转 空字符串

不同版本的spring-web 方法略有区别,本文是5.2.9 是根据 5.3.26 略微修改的

webmvc中注册消息转换


import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.converter.*;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * 消息配置类
 */
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MessageConfiguration implements WebMvcConfigurer {

	private final ObjectMapper objectMapper;

	/**
	 * 使用 JACKSON 作为JSON MessageConverter
	 * 消息转换
	 */
	@Override
	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		converters.removeIf(x -> x instanceof StringHttpMessageConverter || x instanceof AbstractJackson2HttpMessageConverter);
		converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
		converters.add(new ByteArrayHttpMessageConverter());
		converters.add(new ResourceHttpMessageConverter());
		converters.add(new ResourceRegionHttpMessageConverter());
		converters.add(new MappingApiJackson2HttpMessageConverter(objectMapper));
	}

}

注册消息转换module


import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.StreamUtils;

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 *  如果是返回字符串,直接相应,不做 json 处理
 */
public class MappingApiJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {


	public MappingApiJackson2HttpMessageConverter(ObjectMapper objectMapper) {
		super(initWriteObjectMapper(objectMapper), MediaType.APPLICATION_JSON);
	}
	private static ObjectMapper initWriteObjectMapper(ObjectMapper readObjectMapper) {
		// 拷贝 readObjectMapper
		ObjectMapper writeObjectMapper = readObjectMapper.copy();
		// 大数字 转 字符串
		writeObjectMapper.registerModules(ZzNumberModule.INSTANCE);
		// null 处理 todo
		return writeObjectMapper;
	}

	@Override
	protected void writeInternal(@Nullable Object object, @Nullable Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
		// 如果是字符串,直接写出
		if (object instanceof String) {
			Charset defaultCharset = this.getDefaultCharset();
			Charset charset = defaultCharset == null ? StandardCharsets.UTF_8 : defaultCharset;
			StreamUtils.copy((String) object, charset, outputMessage.getBody());
		} else {
			super.writeInternal(object, type, outputMessage);
		}
	}
}

添加序列化


import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;

import java.math.BigDecimal;
import java.math.BigInteger;

/**
 * 大整数序列化为 String 字符串,避免浏览器丢失精度
 * 前端建议采用:
 * bignumber 库: https://github.com/MikeMcl/bignumber.js
 * decimal.js 库: https://github.com/MikeMcl/decimal.js
 */
public class ZzNumberModule extends SimpleModule {
	public static final ZzNumberModule INSTANCE = new ZzNumberModule();

	public ZzNumberModule() {
		super(ZzNumberModule.class.getName());
		// Long 和 BigInteger 采用定制的逻辑序列化,避免超过js的精度
		this.addSerializer(Long.class, BigNumberSerializer.instance);
		this.addSerializer(Long.TYPE, BigNumberSerializer.instance);
		this.addSerializer(BigInteger.class, BigNumberSerializer.instance);
		// BigDecimal 采用 toString 避免精度丢失,前端采用 decimal.js 来计算。
		this.addSerializer(BigDecimal.class, ToStringSerializer.instance);
	}
}

大数值序列化实现


import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;

import java.io.IOException;

/**
 * 大数值序列化,避免超过js的精度,造成精度丢失
 */
@JacksonStdImpl
public class BigNumberSerializer extends NumberSerializer {

	/**
	 * js 最大值为 Math.pow(2, 53),十进制为:9007199254740992
	 */
	private static final long JS_NUM_MAX = 0x20000000000000L;
	/**
	 * js 最小值为 -Math.pow(2, 53),十进制为:-9007199254740992
	 */
	private static final long JS_NUM_MIN = -0x20000000000000L;
	/**
	 * Static instance that is only to be used for {@link Number}.
	 */
	public final static BigNumberSerializer instance = new BigNumberSerializer(Number.class);

	public BigNumberSerializer(Class<? extends Number> rawType) {
		super(rawType);
	}

	@Override
	public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException {
		long longValue = value.longValue();
		if (longValue < JS_NUM_MIN || longValue > JS_NUM_MAX) {
			gen.writeString(value.toString());
		} else {
			super.serialize(value, gen, provider);
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值