统一数据返回处理
-大数字 转 字符串 (前端会丢失精度,所以转字符串)
-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);
}
}
}