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

前言

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

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

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

1实现过程

配置转换器

 
 
 
 
  1. /**
  2.  * @author liuzhihang
  3.  * @date 2021/8/31 16:29
  4.  */
  5. @Configuration
  6. public class WebConfig implements WebMvcConfigurer {
  7.     @Override
  8.     public void addFormatters(FormatterRegistry registry) {
  9.         registry.addConverterFactory(new ConverterFactory() {
  10.             @Override
  11.             public  Converter getConverter(Class targetType) {
  12.                 T[] enums = targetType.getEnumConstants();
  13.                 return source -> {
  14.                     for (T e : enums) {
  15.                         if (e.getCode().equals(source)) {
  16.                             return e;
  17.                         }
  18.                     }
  19.                     throw new IllegalArgumentException("枚举 Code 不正确");
  20.                 };
  21.             }
  22.         });
  23.     }
  24. }

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

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

就这些,很简单的实现。下面介绍下项目的内容和代码,方便理解。

项目代码

  • 请求参数:
 
 
 
 
  1. POST http://localhost:8818/user/listByStatus
  2. Content-Type: application/json
  3. {
  4.   "orderStatus": 1
  5. }
  • Controller
 
 
 
 
  1. /**
  2.  * @author liuzhihang
  3.  * @date 2021/8/30 11:08
  4.  */
  5. @Slf4j
  6. @RestController
  7. @RequestMapping("/user")
  8. public class UserController {
  9.     @Autowired
  10.     private OrderService orderService;
  11.     @PostMapping(value = "/listByStatus")
  12.     public ResultVO listByStatus(@Validated @RequestBody UserRequest request)  {
  13.         log.info("请求参数:{}", request);
  14.         List orderList = orderService.getByOrderStatus(request.getOrderStatus());
  15.         UserResponse response = new UserResponse();
  16.         response.setRecords(orderList);
  17.         log.info("返回参数:{}", response);
  18.         return ResultVO.success(response);
  19.     }
  20. }
  • Entity
 
 
 
 
  1. @Data
  2. public class UserRequest {
  3.     private OrderStatusEnum orderStatus;
  4.     private ViewStatusEnum viewStatus;
  5. }
  6. @Data
  7. public class UserResponse {
  8.     private List records;
  9. }

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

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

为什么要这么用呢?

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

  • 枚举
 
 
 
 
  1. public interface BaseEnum {
  2.     Object getCode();
  3. }
 
 
 
 
  1. public enum OrderStatusEnum implements BaseEnum {
  2.     INIT(0, "初始状态"),
  3.     SUCCESS(1, "成功"),
  4.     FAIL(2, "失败");
  5.     @EnumValue
  6.     @JsonValue
  7.     private final int code;
  8.     private final String desc;
  9.     OrderStatusEnum(int code, String desc) {
  10.         this.code = code;
  11.         this.desc = desc;
  12.     }
  13.     @Override
  14.     public Integer getCode() {
  15.         return code;
  16.     }
  17.     public String getDesc() {
  18.         return desc;
  19.     }
  20. }

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

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

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

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

效果

测试结果

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

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

具体说明

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

 
 
 
 
  1. public interface ConverterFactory {
  2.   Converter getConverter(Class targetType);
  3. }
  • S 就是传入的字段类型(数字,字符串)
  • R 是要转换为的类型(枚举)
  • T 继承了 R,其实就是参数对象中字段的类型

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

 
 
 
 
  1. @FunctionalInterface
  2. public interface Converter {
  3.  @Nullable
  4.  T convert(S source);
  5. }

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

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

2优化

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

 
 
 
 
  1. @Configuration
  2. public class WebConfig implements WebMvcConfigurer {
  3.     @Autowired
  4.     private LogInterceptor logInterceptor;
  5.     @Autowired
  6.     private AppTokenInterceptor appTokenInterceptor;
  7.     @Autowired
  8.     private EnumConverterFactory enumConverterFactory;
  9.     @Override
  10.     public void addInterceptors(InterceptorRegistry registry) {
  11.         // 日志
  12.         registry.addInterceptor(logInterceptor)
  13.                 .addPathPatterns("/**");
  14.         // app token校验
  15.         registry.addInterceptor(appTokenInterceptor)
  16.                 .addPathPatterns("/app/**");
  17.     }
  18.     @Override
  19.     public void addFormatters(FormatterRegistry registry) {
  20.         
  21.         // 枚举转换
  22.         registry.addConverterFactory(enumConverterFactory);
  23.     }
  24. }

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

 
 
 
 
  1. @Component
  2. public class EnumConverterFactory implements ConverterFactory {
  3.     @Override
  4.     public  Converter getConverter(Class targetType) {
  5.         return new EnumConverter<>(targetType);
  6.     }
  7. }
  8. public class EnumConverter implements Converter {
  9.     private final Class targetType;
  10.     public EnumConverter(Class targetType) {
  11.         this.targetType = targetType;
  12.     }
  13.     @Override
  14.     public T convert(Object source) {
  15.         for (T e : targetType.getEnumConstants()) {
  16.             if (e.getCode().equals(source)) {
  17.                 return e;
  18.             }
  19.         }
  20.         throw new IllegalArgumentException("枚举 Code 不正确");
  21.     }
  22. }

3总结

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

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

网站名称:SpringBoot使用转换器将前端参数转换为枚举
文章源于:http://www.shufengxianlan.com/qtweb/news13/351363.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联