SpringBoot前后端参数优化
1.去除前端入参的两端空格
2.前后端枚举值互相转换
3.处理后端NULL值
全局序列化操作,方便前后端传参,优化后端接口的调用体验,减少纠纷。
包含内容
1~3都要用到Jackson2ObjectMapperBuilderCustomizer
进行处理,首先定义一个JacksonConfig
类,配置Bean
1 2 3 4 5 6 7 8
| @Configuration public class JacksonConfig {
@Bean public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { return new Jackson2ObjectMapperBuilder().build(); } }
|
1、前端传入的字符串类型参数,去除两端空格
使用@InitBinder
注解,它用来在请求到达controller方法前进行数据绑定时进行操作,还可以做数据校验、参数类型转换等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @ControllerAdvice(basePackages = {"包路径"}) public class GlobalStringTrimConfig {
@InitBinder public void initBinder(WebDataBinder binder) { StringTrimmerEditor propertyEditor = new StringTrimmerEditor(false); binder.registerCustomEditor(String.class, propertyEditor); } }
|
修改Jackson2ObjectMapperBuilderCustomizer
,去除post参数空格,添加如下配置:
1 2 3 4 5 6 7 8 9 10 11 12
| @Bean public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { return bu -> { bu.deserializerByType(String.class, new StdScalarDeserializer<>(String.class) { @Override public String deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException { return StringUtils.trimWhitespace(jsonParser.getValueAsString()); } }); }; }
|
2、前端传入的枚举值,转换成后端枚举对象
创建一个公共的接口BaseEnum
,将来所有需要转换的枚举类,都实现该接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
public interface BaseEnum {
static <T extends BaseEnum> T getEnum(Class<T> type, Object codeOrName) { T[] enums = type.getEnumConstants(); for (T em : enums) { if (em.getStrCode().equals(codeOrName.toString()) || em.name().equals(codeOrName.toString())) { return em; } } return null; }
String getStrCode();
String name(); }
|
创建枚举类序列化方法
1 2 3 4 5 6 7 8
| public class BaseEnumSerializer extends JsonSerializer<BaseEnum> { @Override public void serialize(BaseEnum value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeNumber(value.getStrCode()); gen.writeStringField(gen.getOutputContext().getCurrentName() + "Text", value.name()); } }
|
创建整型和字符串类型的枚举转换器,用于数字或字符串类型转成枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class IntegerToEnumConverter<T extends BaseEnum> implements Converter<Integer, T> { private Map<String, T> enumMap = new HashMap<>();
public IntegerToEnumConverter(Class<T> enumType) { T[] enums = enumType.getEnumConstants(); for (T e : enums) { enumMap.put(e.getStrCode(), e); } }
@Override public T convert(Integer source) { T t = enumMap.get(source); if (Objects.isNull(t)) { throw new IllegalArgumentException("无法匹配对应的枚举类型"); } return t; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class StringToEnumConverter<T extends BaseEnum> implements Converter<String, T> { private Map<String, T> enumMap = Maps.newHashMap();
public StringToEnumConverter(Class<T> enumType) { T[] enums = enumType.getEnumConstants(); for (T e : enums) { enumMap.put(e.getStrCode().toString(), e); } }
@Override public T convert(String source) { T t = enumMap.get(source); if (Objects.isNull(t)) { throw new IllegalArgumentException("无法匹配对应的枚举类型"); } return t; } }
|
创建对应的工厂类,并将工厂类注册到mvc全局配置中
Integer类型转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class IntegerCodeToEnumConverterFactory implements ConverterFactory<Integer, BaseEnum> { private static final Map<Class, Converter> CONVERTERS = new HashMap<>();
@Override public <T extends BaseEnum> Converter<Integer, T> getConverter(Class<T> targetType) { Converter<Integer, T> converter = CONVERTERS.get(targetType); if (converter == null) { converter = new IntegerToEnumConverter<>(targetType); CONVERTERS.put(targetType, converter); } return converter; } }
|
String类型转换
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class StringCodeToEnumConverterFactory implements ConverterFactory<String, BaseEnum> { private static final Map<Class, Converter> CONVERTERS = Maps.newHashMap();
@Override public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) { Converter<String, T> converter = CONVERTERS.get(targetType); if (converter == null) { converter = new StringToEnumConverter<>(targetType); CONVERTERS.put(targetType, converter); } return converter; } }
|
注册到mvc配置
1 2 3 4 5 6 7 8 9
| @Configuration public class WebConfigurer implements WebMvcConfigurer {
@Override public void addFormatters(FormatterRegistry registry) { registry.addConverterFactory(new IntegerCodeToEnumConverterFactory()); registry.addConverterFactory(new StringCodeToEnumConverterFactory()); } }
|
最后配置到Jackson2ObjectMapperBuilderCustomizer
中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Bean public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { return bu -> { bu.serializerByType(BaseEnum.class, baseEnumSerializer); bu.deserializerByType(BaseEnum.class, baseEnumDeserializer); bu.deserializerByType(String.class, new StdScalarDeserializer<>(String.class) { @Override public String deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException { return StringUtils.trimWhitespace(jsonParser.getValueAsString()); } }); }; }
|
3、后端返回值为null时,对具体类型进行相关处理
针对null值处理,有一个专门的序列化类NullValueSerializer
进行操作,先创建一个自定义的空值序列化器,再注册到ObjectMapper
中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
public class NullValueSerializer extends JsonSerializer<Object> {
@Override public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { String fieldName = gen.getOutputContext().getCurrentName(); Field field = FieldUtils.getDeclaredField(gen.getCurrentValue().getClass(), fieldName, true); if (Objects.nonNull(field)) { if (Collection.class.isAssignableFrom(field.getType())) { gen.writeStartArray(); gen.writeEndArray(); return; } if (Objects.equals(field.getType(), String.class)){ gen.writeString(""); return; } } gen.writeNull(); } }
|
在ObjectMapper
类中注册NullValueSerializer
序列化器
1 2 3 4 5 6 7
| @Bean @Primary public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) { ObjectMapper objectMapper = builder.createXmlMapper(false).build(); objectMapper.getSerializerProvider().setNullValueSerializer(new NullValueSerializer()); return objectMapper; }
|
测试结果
创建Sex
枚举类,实现BaseEnum
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
@Getter public enum Sex implements BaseEnum { MAN(1, "男"), WOMAN(2, "女");
private Integer code; private String desc;
Sex(Integer code, String desc) { this.code = code; this.desc = desc; }
@Override public String getStrCode() { return code.toString(); } }
|
创建user
类,内容如下
1 2 3 4 5 6 7 8 9
| @Data public class User {
private String name; private Sex sex; private Integer age; private String password; private List<String> deptList; }
|
添加测试接口
1 2 3 4 5
| @PostMapping("/test") public String test(@RequestBody User user, String password) { user.setPassword(password); return JSON.toJSONString(user); }
|
调用接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| { "name": " 张三 ", "sex": "1", "password": "1123111 ", "age": 50 }
{ "name": "张三", "sex": 1, "sexText": "MAN", "age": 50, "password": "123121", "deptList": [] }
|