当前位置:首页 > 域名

SpringBoot 使用转换器将前端参数转换为枚举

前言

最近遇到一个小伙伴问前端枚举转换问题,使用才意识到可以通过转换器(Converter)自动将前端传入的转换转换字段值使用枚举接收。

我自己捣鼓了一番,器将前端现在记录笔记分享一下!有兴趣的参数小伙伴可以自己尝试一下!

这里使用的是 MyBatis-Plus 和 SpringBoot 2.3.4.RELEASE

1实现过程

配置转换器

/**  * @author liuzhihang  * @date 2021/8/31 16:29  */ @Configuration public class WebConfig implements WebMvcConfigurer {      @Override     public void addFormatters(FormatterRegistry registry) {          registry.addConverterFactory(new ConverterFactory<Object, BaseEnum>() {              @Override             public <T extends BaseEnum> Converter<Object, T> getConverter(Class<T> targetType) {                  T[] enums = targetType.getEnumConstants();                 return source -> {                      for (T e : enums) {                          if (e.getCode().equals(source)) {                              return e;                         }                     }                     throw new IllegalArgumentException("枚举 Code 不正确");                 };             }         });     } } 

直接在 WebMvcConfigurer 里实现 addFormatters 方法即可,然后 new 一个 ConverterFactory。为枚

WebMvcConfigurer 相信大家都不陌生,使用一般添加一些拦截器,转换转换通用校验 token、器将前端日志等等都会用到。参数具体可以参考这篇文章:几行代码轻松实现跨系统传递 traceId,为枚再也不用担心对不上日志了!,使用里面有一些其他的转换转换应用。

就这些,器将前端很简单的参数实现。下面介绍下项目的为枚内容和代码,方便理解。

项目代码

请求参数: POST http://localhost:8818/user/listByStatus Content-Type: application/json {    "orderStatus": 1 }  Controller /**  * @author liuzhihang  * @date 2021/8/30 11:08  */ @Slf4j @RestController @RequestMapping("/user") public class UserController {      @Autowired     private OrderService orderService;     @PostMapping(value = "/listByStatus")     public ResultVO<UserResponse> listByStatus(@Validated @RequestBody UserRequest request)  {          log.info("请求参数:{ }", request);         List<TransOrder> orderList = orderService.getByOrderStatus(request.getOrderStatus());         UserResponse response = new UserResponse();         response.setRecords(orderList);         log.info("返回参数:{ }", response);         return ResultVO.success(response);     } }  Entity @Data public class UserRequest {      private OrderStatusEnum orderStatus;     private ViewStatusEnum viewStatus; } @Data public class UserResponse {      private List<TransOrder> records; } 

Web 传入 orderStatus 为 1,而后端接收对象是 UserRequest 的 orderStatus 字段是个 OrderStatusEnum 类型的枚举。

这里就需要自动将数字类型的字段转换为枚举字段。这个枚举会直接通过 MyBatis-Plus 查询。

为什么要这么用呢?

其实原因很简单,使用枚举限制数据库字段的源码库类型,比如数据库状态只有 0、1、2,那就和代码里的枚举对应起来。防止传入其他值。

枚举 public interface BaseEnum {      Object getCode(); }  public enum OrderStatusEnum implements BaseEnum {      INIT(0, "初始状态"),     SUCCESS(1, "成功"),     FAIL(2, "失败");     @EnumValue     @JsonValue     private final int code;     private final String desc;     OrderStatusEnum(int code, String desc) {          this.code = code;         this.desc = desc;     }     @Override     public Integer getCode() {          return code;     }     public String getDesc() {          return desc;     } } 

这里先声明接口 BaseEnum,所有的枚举都继承这个接口,并实现 getCode 方法。

@EnumValue:MyBatis-Plus 的枚举,和数据库字段映射用的

@JsonValue:返回给前端时,这个枚举字段序列化时,返回参数只显示 code。

这样就可以实现效果,请求参数为数字,接收对象字段为枚举,返回字段也是 code。

效果

测试结果

测试结果经过验证,是可以胜任传入数值和字符串的。

也可以结合异常处理器,返回通用异常。具体怎么用查一查 @ExceptionHandler 就知道了。

具体说明

在 addFormatters 方法中可以看到 registry.addConverterFactory() 接收的是一个 ConverterFactory 对象。

public interface ConverterFactory<S, R> {   <T extends R> Converter<S, T> getConverter(Class<T> targetType); }  S 就是服务器租用传入的字段类型(数字,字符串) R 是要转换为的类型(枚举) T 继承了 R,其实就是参数对象中字段的类型

在 ConverterFactory 的 getConverter 方法则需要返回一个实际的转换器 Converter

@FunctionalInterface public interface Converter<S, T> {   @Nullable  T convert(S source); } 

convert 方法的入参是一个 source,就是要转换为什么类型的,这里就是数字/字符串,然后返回一个枚举即可。

注意这里加了 @FunctionalInterface 就意味着这里是可以用 lambda 表达式的。

2优化

一般 WebConfig 中除了实现 addFormatters 方法外,还会实现 addInterceptors 等等,这样写难免会很长,所以可以改为下面这种。

@Configuration public class WebConfig implements WebMvcConfigurer {      @Autowired     private LogInterceptor logInterceptor;     @Autowired     private AppTokenInterceptor appTokenInterceptor;     @Autowired     private EnumConverterFactory enumConverterFactory;     @Override     public void addInterceptors(InterceptorRegistry registry) {          // 日志         registry.addInterceptor(logInterceptor)                 .addPathPatterns("/**");         // app token校验         registry.addInterceptor(appTokenInterceptor)                 .addPathPatterns("/app/**");     }     @Override     public void addFormatters(FormatterRegistry registry) {          // 枚举转换         registry.addConverterFactory(enumConverterFactory);     } } 

这种就需要咱们创建 EnumConverterFactory 类并实现 ConverterFactory 接口了,还得注入到 Spring 容器中

@Component public class EnumConverterFactory implements ConverterFactory<Object, BaseEnum> {      @Override     public <T extends BaseEnum> Converter<Object, T> getConverter(Class<T> targetType) {          return new EnumConverter<>(targetType);     } } public class EnumConverter<T extends BaseEnum> implements Converter<Object, T> {      private final Class<T> targetType;     public EnumConverter(Class<T> targetType) {          this.targetType = targetType;     }     @Override     public T convert(Object source) {          for (T e : targetType.getEnumConstants()) {              if (e.getCode().equals(source)) {                  return e;             }         }         throw new IllegalArgumentException("枚举 Code 不正确");     } } 

3总结

当然这里也有一些其他的优化点,比如可以使用缓存将 Convert 缓存起来。

不过我也遇到一个其他的问题,就是我 debug 断点竟然一直没有断到转换器中,源码下载不知道有没有小伙伴尝试过?

分享到:

滇ICP备2023006006号-16