写了一套几乎无敌的参数校验组件!!!

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料: 

12f3328bbe088ab27d47465361a93523.gif

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号、CRM 等等功能:

  • Boot 仓库:https://gitee.com/zhijiantianya/ruoyi-vue-pro

  • Cloud 仓库:https://gitee.com/zhijiantianya/yudao-cloud

  • 视频教程:https://doc.iocoder.cn

【国内首批】支持 JDK 21 + SpringBoot 3.2.2、JDK 8 + Spring Boot 2.7.18 双版本 

来源:程序员阿杆

a4c049c50ba7bddb4bc7225ba1468e18.jpeg


参数校验这个东西,很多情况下都是比较简单的,用 @NotNull@Size 等注解就可以解决绝大多数场景,但也有一些场景是这些基本注解解决不了的,只能用一些其他的方式处理,这样就导致参数校验变成了多层,其实是不利于代码维护的。

于是乎,我写了一套几乎可以满足任何场景的参数校验组件,非常好用,安利给大家。

GitHub地址文末获取!

💡 它解决了什么问题?

  • 枚举值字段校验:

    @SpelAssert(assertTrue = " T(cn.sticki.enums.UserStatusEnum).getByCode(#this.userStatus) != null ", message = "用户状态不合法")
    private Integer userStatus;
  • 多字段联合校验:

    @NotNull
    private Integer contentType;
    
    @SpelNotNull(condition = "#this.contentType == 1", message = "语音内容不能为空")
    private Object audioContent;
    
    @SpelNotNull(condition = "#this.contentType == 2", message = "视频内容不能为空")
    private Object videoContent;
  • 复杂逻辑校验,调用静态方法:

    // 中文算两个字符,英文算一个字符,要求总长度不超过 10
    // 调用外部静态方法进行校验
    @SpelAssert(assertTrue = "T(cn.sticki.util.StringUtil).getLength(#this.userName) <= 10", message = "用户名长度不能超过10")
    private String userName;
  • 调用 Spring Bean(需要使用 @EnableSpelValidatorBeanRegistrar 开启Spring Bean支持):

    // 这里只是简单举例,实际开发中不建议这样判断用户是否存在
    @SpelAssert(assertTrue = "@userService.getById(#this.userId) != null", message = "用户不存在")
    private Long userId;
  • 更多使用场景,欢迎探索和补充!

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

📝 特点

  • 强大的参数校验功能,几乎支持所有场景下的参数校验。

  • 扩展自 javax.validation 包,只新增不修改,无缝集成到项目中。

  • 基于 SpEL(Spring Expression Language) 表达式,支持复杂的校验逻辑。

  • 支持调用 Spring Bean,可在表达式中使用注入过的 Spring Bean。

  • 校验时基于整个对象,支持对象内字段间的校验逻辑。

  • 支持自定义校验注解,可根据业务需求自定义校验逻辑。

  • 无需额外的异常处理,校验失败时会上报到 javax.validation 的异常体系中。

  • 简单易用,使用方式几乎与 javax.validation 一致,学习成本低,上手快。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

🎈 环境

目前仅测试了 JDK8 环境,理论上来说 JDK8+ 应该都是支持的。

📦 快速开始

  • 添加依赖

    Latest Version: 0.0.2-beta

    <dependency>
        <groupId>cn.sticki</groupId>
        <artifactId>spel-validator</artifactId>
        <version>Latest Version</version>
    </dependency>
    
    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>${hibernate-validator.version}</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>${spring-boot-starter-web.version}</version>
    </dependency>
  • 在接口参数上使用 @Valid@Validated 注解

    @RestController
    @RequestMapping("/example")
    public class ExampleController {
    
      /**
       * 简单校验示例
       */
      @PostMapping("/simple")
      public Resp<Void> simple(@RequestBody @Valid SimpleExampleParamVo simpleExampleParamVo) {
        return Resp.ok(null);
      }
    
    }
  • 在实体类上使用 @SpelValid 注解,同时在需要校验的字段上使用 @SpelNotNull 等约束注解

    @Data
    @SpelValid
    public class SimpleExampleParamVo {
    
      @NotNull
      private Boolean switchAudio;
    
      /**
       * 当 switchAudio 为 true 时,校验 audioContent,audioContent 不能为null
       */
      @SpelNotNull(condition = "#this.switchAudio == true", message = "语音内容不能为空")
      private Object audioContent;
    
    }
  • 添加全局异常处理器,处理校验异常

    @RestControllerAdvice
    public class ControllerExceptionAdvice {
    
      @ExceptionHandler({BindException.class, MethodArgumentNotValidException.class})
      public Resp<Void> handleBindException(BindException ex) {
        String msg = ex.getFieldErrors().stream()
            .map(error -> error.getField() + " " + error.getDefaultMessage())
            .reduce((s1, s2) -> s1 + "," + s2)
            .orElse("");
        return new Resp<>(400, msg);
      }
    
    }
  • 发起请求,即可看到校验结果

    • 示例一:@SpelNotNull 校验不通过

      请求体:

      {
        "switchAudio": true,
        "audioContent": null
      }

      响应体

      {
        "code": 400,
        "message": "audioContent 语音内容不能为空",
        "data": null
      }
  • 示例二:校验通过

    请求体

    {
      "switchAudio": false,
      "audioContent": null
    }

    响应体

    {
      "code": 200,
      "message": "成功",
      "data": null
    }
  • 示例三:@NotNull 校验不通过

    请求体

    {
      "switchAudio": null,
      "audioContent": null
    }

    响应体

    {
      "code": 400,
      "message": "switchAudio 不能为null",
      "data": null
    }

📖 使用指南

注意:本组件的目的不是代替 javax.validation 的校验注解,而是作为一个扩展,方便某些场景下的参数校验。能够使用 javax.validation 的场景就不要使用 spel-validator ,因为 spel-validator 会有一定的性能损耗。

开启约束校验

需要满足以下两个条件,才会对带注解的元素进行校验:

  1. 在接口参数上使用 @Valid@Validated 注解

  2. 在实体类上使用 @SpelValid 注解

如果只满足第一个条件,那么只会对带 @NotNull@NotEmpty@NotBlank 等注解的元素进行校验。

如果只满足第二个条件,那么不会对任何元素进行校验。

这是因为 @SpelValid 注解是基于 javax.validation.Constraint 实现的,只有在 @Valid@Validated 注解的支持下才会生效。而 spel-validator 提供的约束注解是基于 @SpelValid 进行扫描校验的,只有在 @SpelValid 注解生效的情况下才会执行约束校验。

使用约束注解

目前支持的约束注解有:

注解说明对标 javax.validation
@SpelAssert逻辑断言校验
@SpelNotNull非 null 校验@NotNull
@SpelNotEmpty集合、字符串、数组大小非空校验@NotEmpty
@SpelNotBlank字符串非空串校验@NotBlank
@SpelNull必须为 null 校验@Null
@SpelSize集合、字符串、数组长度校验@Size

每个约束注解都包含三个默认的属性:

  • message:校验失败时的提示信息。

  • group:分组条件,支持 SpEL 表达式,当分组条件满足时,才会对带注解的元素进行校验。

  • condition:约束开启条件,支持 SpEL 表达式,当 表达式为空 或 计算结果为true 时,才会对带注解的元素进行校验。

调用 Spring Bean

默认情况下,解析器无法识别 SpEL 表达式中的 Spring Bean。

如果需要在 SpEL 表达式中调用 Spring Bean,需要在启动类上添加 @EnableSpelValidatorBeanRegistrar 注解, 开启 Spring Bean 支持。

@EnableSpelValidatorBeanRegistrar
@SpringBootApplication
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

}

自定义约束注解

  1. 在注解上使用 @SpelConstraint ,并指定验证器。

  2. 然后给注解添加上三个固定的字段 message 、group、condition。

  3. 实现验证器

6a391a2139be78949254e85b495b2e44.png
自定义示例约束示例-定义约束注解
e848cc394848320e72444f71d67553a3.png
自定义约束示例-实现验证器

具体实现方式可以参考 cn.sticki.validator.spel.SpelConstraint 类。

如果你使用过 javax.validation 的自定义约束注解,那么你会发现 SpEL Validator 的自定义约束注解几乎与 javax.validation一致。

📦 示例项目

我也写了一个简单的示例项目,但目前还不太完善,只有基本的使用方法:

  • https://github.com/stick-i/spel-validator-example

最后

关于性能

性能上我目前还没有进行测试,但代码里使用了很多的反射,会有一定的损耗,后面我准备多加一些缓存,尽量降低性能上的影响。

一个奇怪的现象

在项目中使用SpEL的时候,有一个很怪异的现象:

9a68bd19de6f3aa4c84ee519ffca6241.png

我给这两个字段都标记了 @Language("SpEL"),但是只有 condition 可以识别,我不知道这算不算idea的bug,我目前使用的idea版本是 IntelliJ IDEA 2024.1 (Ultimate Edition),有懂的朋友请帮忙解答一下。

GitHub地址:https://github.com/stick-i/spel-validator


欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

b153d80e23bf7805658fe4ad5870f1b0.png

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

a541482cbb4cc0eeeebef6d2f7d672ac.png

7b27f2f393b472373b9df4dd55ec3316.png13249a735f588380ac46f07c4d2c3520.pngab90289e784645ff02e9bc7f90db0041.pnga71714c294b8f93c8db6dc8b2f74eea2.png

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值