这是一套几乎无敌的参数校验组件!SpEL Validator 预览版发布


前言

大家好,我是阿杆,不是阿轩。

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

于是乎,我写了一套几乎可以满足任何场景的参数校验组件,名为 SpEL Validator ,安利给大家。

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

官方文档:https://spel-validator.sticki.cn/

💡 它解决了什么问题?

  • 枚举值字段校验:

    @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;
    
  • 更多使用场景,欢迎探索和补充!

📝 特点

  • 强大的参数校验功能,几乎支持所有场景下的参数校验。
  • 扩展自 javax.validation 包,只新增不修改,无缝集成到项目中。
  • 基于 SpEL(Spring Expression Language) 表达式,支持复杂的校验逻辑。
  • 支持调用 Spring Bean,可在表达式中使用注入过的 Spring Bean。
  • 校验时基于整个对象,支持对象内字段间的校验逻辑。
  • 支持自定义校验注解,可根据业务需求自定义校验逻辑。
  • 无需额外的异常处理,校验失败时会上报到 javax.validation 的异常体系中。
  • 简单易用,使用方式几乎与 javax.validation 一致,学习成本低,上手快。

🎈 环境

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

📦 快速开始

  • 添加依赖

    Latest Version: 0.2.0-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. 实现验证器

 

具体实现方式可以参考官方文档。

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

📦 示例项目

我也写了一个简单的示例项目,有一些基本的使用方法:

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

最后

贴一下GitHub地址:https://github.com/stick-i/spel-validator (顺手点个star呀~)

欢迎大家进行体验,有任何疑问欢迎一起讨论。


相關推薦

2024-07-10

编写Redis代码逻辑 ORM框架 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多 例如多租户插件 分页插件 乐观锁插件等等 采用 Mybatis 基于XML需要手写SQL SQL监控 采用 p6spy 可输出完整SQL与执行时间监控

2024-09-24

端): 流程提交增加判空 App组件库(前端): 人员选择器参数修改 我的平台(前端): 支持插件资源下载 画图套件(前端): 支持节点格式化占位符 画图套件(前端): 工艺图tag数据绑定列表高度动态计算 画图套件(前端):

2024-09-27

le监听器变量新增FlowParams字段,方便开始监听器全局传递参数 [feat] 终止新增对开始和完成监听器的支持 [update] springboot项目的条件表达式默认支持spel [update] 历史记录改为单条保存,删除重复代码 [update] 修改FlowUserDao的bean

2023-11-15

编写Redis代码逻辑 ORM框架 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多 例如多租户插件 分页插件 乐观锁插件等等 采用 Mybatis 基于XML需要手写SQL SQL监控 采用 p6spy 可输出完整SQL与执行时间监控

2023-01-13

用户 避免多次 join 查询 update 优化 删除 vue3 模板无用参数 update 优化 xss 包装器 变量命名错误 update 优化 重构 ExcelUtil 全导出方法支持 OutputStream 流导出 不局限于 response update 优化 maven 地址切换回 aliyun 仓库 update

2024-06-26

支持 错误处理: 优化错误信息处理, 支持多语言错误 Validator: 简单易用的校验器, 只需添加 tag 即可校验 三端代码生成: 支持三端代码生成,生成 API, RPC 和 web 端的 CRUD 代码 RPC接口分组: rpc logic group 分组 proto文件拆分: 支

2022-10-08

微前端更适合大型前端拆解、多前端项目整合情况。构建一套完善的微前端应用,研发投入大、潜在问题多、使用复杂度高,并不适合本项目目前大多数用户的实际用途。采用一些性价比更高的替代方案,比如说 Nginx 的多应用

2023-11-28

编写Redis代码逻辑 ORM框架 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多 例如多租户插件 分页插件 乐观锁插件等等 采用 Mybatis 基于XML需要手写SQL SQL监控 采用 p6spy 可输出完整SQL与执行时间监控

2024-07-16

支持 错误处理: 优化错误信息处理, 支持多语言错误 Validator: 简单易用的校验器, 只需添加 tag 即可校验 三端代码生成: 支持三端代码生成,生成 API, RPC 和 web 端的 CRUD 代码 RPC 接口分组: rpc logic group 分组 proto 文件拆分:

2023-11-16

编写Redis代码逻辑 ORM框架 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多 例如多租户插件 分页插件 乐观锁插件等等 采用 Mybatis 基于XML需要手写SQL SQL监控 采用 p6spy 可输出完整SQL与执行时间监控

2023-01-14

删除主 sql 内无用数据 update 优化 删除 vue3 模板无用参数 update 优化 重构 ExcelUtil 全导出方法支持 OutputStream 流导出 不局限于 response update 优化 maven 地址切换回 aliyun 仓库 update 优化 springdoc 配置鉴权头写死问题 增加

2023-11-28

编写Redis代码逻辑 ORM框架 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多 例如多租户插件 分页插件 乐观锁插件等等 采用 Mybatis 基于XML需要手写SQL SQL监控 采用 p6spy 可输出完整SQL与执行时间监控

2024-09-28

启动太多服务,开发机器扛不住? 4. 同时维护2套源码(一套单体版,一套微服务版),且保持代码同步的难度大,工作量大! 解决方案: 《灯灯》4.22.0版本开始,将原来的lamp-cloud项目和lamp-boot项目合并为一个项目,合并后

2023-08-09

ArkUI是一套构建分布式应用的声明式UI开发框架。它具备简洁自然的UI信息语法、丰富的UI组件、多维的状态管理,以及实时界面预览等相关能力,帮助您提升应用开发效率,并能在多种设备上实现生动而流畅的用户体验。详情可