Hefery 的个人网站

Hefery's Personal Website

Contact:hefery@126.com
  menu
73 文章
0 浏览
1 当前访客
ღゝ◡╹)ノ❤️

Validator 参数校验

Bean Validator简介

JavaBean 验证主要是 POJO、Dao、Service、Controller

JCP和JSR:JCP(Java Community Process)使各方参与定义 Java 特征和未来版本的正式过程,JCP 使用 JSR(Java Spectification Request,Java规范请求)作为正式规范文档,描述被提议加入到 Java 体系中的规范和技术

  • JSR303 — Bean Validator 1.0 — Hibernate Validator 4.3.1.Final
  • JSR349 — Bean Validator 1.1 — Hibernate Validator 5.1.1.Final
  • JSR380 — Bean Validator 2.0 — Hibernate Validator 6.0.1.Final

Spring Validator 在 Hibernate Validator 基础上进行二次封装,以满足 Spring 环境中更简单、高效地对数据进行验证

Spring Validator使用

基础校验

基础校验流程

  • pom.xml 文件引入 hibernate-validator 依赖

    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
    </dependency>
    
  • 进行校验的 Controller 标记 @Validated 注解

    • URL以“?”拼接参数:/check-param/url-params-01?name=hefery&age=18&gender=man

      @RequestMapping(value = "/url-params-01", method = RequestMethod.GET)
      public String testUrlParams01(
              @RequestParam("name") String name,
              @RequestParam("age") @Range(min = 1, max = 18, message = "年龄在1~18之间") Integer age,
              @RequestParam(value = "gender", required = false) String gender
      ) {
          UserDTO user = new UserDTO();
          user.setName(name);
          user.setAge(age);
          user.setGender(gender);
          return JSON.toJSONString(user);
      }
      
    • URL以“/”拼接参数:/check-param/url-params-03/hefery/18/man

      @RequestMapping(value = {"/url-params-03/{name}/{age}/{gender}","/url-params-03/{name}/{gender}"}, method = RequestMethod.POST)
      public String testUrlParams03(
              @PathVariable("name") String name,
              @PathVariable(value = "age", required = false) @Range(min = 1, max = 18, message = "年龄在1~18之间") Integer age,
              @PathVariable("gender") String gender
      ) {
          UserDTO user = new UserDTO();
          user.setName(name);
          user.setGender(gender);
          user.setAge(age);
          return JSON.toJSONString(user);
      }
      
    • 参数写在请求体:/check-param/json-params-01
      在需要校验的 UserDTO 前添加 @Valid 或 @Validated 注解

      @RequestMapping(value = "/json-params-01", method = RequestMethod.GET)
      public String testJsonParams01(@RequestBody @Valid UserDTO user) {
          return JSON.toJSONString(user);
      }
      
      public class UserDTO {
          private String name;
      
          @Range(min = 1, max = 18, message = "年龄在1~18之间")
          private Integer age;
      
          private String gender;
      }
      

基础校验注解

  • 空值类

    • @Null
    • @NotNull:@NotNull(message = "用户ID不能为空")
    • @NotEmpty:@NotEmpty(message = "用户名不能为空")
    • @NotBlank:@NotBlank(message = "密码不能为空")
  • 范围类:

    • @Length:

      • min:最小长度
      • max:最大长度
    • @Min:@Min(value = 18, message = "年龄不能小于18岁")

      • value:最小值
    • @Max:@Max(value = 30, message = "年龄不能超过30岁")

      • value:最大值
    • @Size:@Size(min = 1, max = 30, message = "不能少于1位好友")

      • min:最小长度
      • max:最大长度
    • @Range:@Range(min = 1, max = 30, message = "必须在 1~30 之间")

  • 值判断:

    • @Digits:数字
    • @AssertTrue:必须是 true
    • @AssertFalse:必须是 false
  • 日期类:

    • @Future:将来的日期
    • @Past:@Past(message = "日期不能为当前日期和未来日期")
  • 其他类:

    • @Email:@Email(message = "邮箱必须有效")
    • @Pattern:符合正则
    • @Valid
  • 高级:

    • 分组:适用于不同场景的使用
      • 在实体类中定义场景接口
        // 登录场景
        public interface LoginGroup {}
        // 注册场景
        public interface RegisterGroup {}
        
      • 在场景所需字段的注解中添加分组信息
        /** 用户名 */
        @NotEmpty(message = "用户名不能为空", groups = UserInfo.LoginGroup.class)
        private String username;
        
        /** 密码 */
        @NotBlank(message = "密码不能为空", groups = UserInfo.LoginGroup.class)
        @Length(min = 6, max = 20, message = "密码成都不能少于6位,以及不能多于20位")
        private String password;
        
      • 验证时,加入场景接口
        // 验证器验证 userInfo 对象
        resultSet = validator.validate(
        	userInfo,
                UserInfo.RegisterGroup.class,
                UserInfo.LoginGroup.class
         );
        
    • 分组排序:决定先进行 分组1 校验,再进行 分组2 校验
      • 在实体类中定义分组排序的接口
        @GroupSequence({
                    LoginGroup.class,
                    RegisterGroup.class,
                    Default.class
            })
            public interface Group {}
        
      • 在场景所需字段的注解中添加分组信息
        // 验证器验证 userInfo 对象
        resultSet = validator.validate(
        	userInfo,
                UserInfo.Group.class
         );
        

@NotEmpty、@NotBlank区别:

  • @NotEmpty(message = "用户名不能为空") // 指的是 userInfo.setUsername("") 不会报错
    空的:没有内容
  • @NotBlank(message = "密码不能为空") // 指的是 userInfo.setPassword("") 会报错
    空白:自动去掉字符串前后空格后验证是否为空

级联校验

校验 Bean 中嵌套的 Bean,如 UserDTO 中的 SchoolDTO 中的 schoolName 属性(外层加@Valid,里层加基础校验)

  • 在 SchoolDTO 中的 schoolName 属性添加基础校验注解
  • 在 UserDTO 中的 schoolDTO 属性标注 @Valid 注解
@RequestMapping(value = "/json-params-02", method = RequestMethod.GET)
public String testJsonParams02(@RequestBody @Valid UserDTO user) {
    return JSON.toJSONString(user);
}

public class UserDTO {

    private String name;

    @Range(min = 1, max = 18, message = "年龄在1~18之间")
    private Integer age;

    private String gender;

    @Valid
    private SchoolDTO schoolDTO;

}

public class SchoolDTO {

    @Length(min = 2, max = 3, message = "schoolName长度在2~3之间")
    private String schoolName;

    private String schoolAddress;

}

分组校验

自定义校验

// 校验执行器
ExecutableValidator executableValidator = validator.forExecutables();
  • 校验参数

    public class UserInfoService {
    
        /**
         * UserInfo 作为输入参数
         * @param userInfo
         */
        public void setUserInfo(@Valid UserInfo userInfo) {}
    
    }
    
    @Test
    public void test() throws NoSuchMethodException {
            // 初始化验证器
            Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
            // 校验执行器
            ExecutableValidator executableValidator = validator.forExecutables();
            // 待验证对象
            UserInfoService userInfoService = new UserInfoService();
            // 待验证方法
            Method setUserInfoMethod = userInfoService.getClass().getMethod("setUserInfo", UserInfo.class);
            // 方法输入参数
            Object[] paramObjects = new Object[]{new UserInfo()};
    
            // 对方法的输入参数进行校验
            Set<ConstraintViolation<UserInfoService>> otherSet;
            otherSet = executableValidator.validateParameters(
                    userInfoService,
                    setUserInfoMethod,
                    paramObjects
            );
    
    }
    
  • 校验返回值

    public class UserInfoService {
    
        /**
         * UserInfo 作为返回值
         * @return
         */
        public UserInfo getUserInfo() {
            return new UserInfo();
        }
    
    }
    
    @Test
    public void test() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            // 初始化验证器
            Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
            // 校验执行器
            ExecutableValidator executableValidator = validator.forExecutables();
            // 待验证对象
            UserInfoService userInfoService = new UserInfoService();
            // 待验证方法
            Method getUserInfoMethod = userInfoService.getClass().getMethod("getUserInfo");
            // 待验证返回值
            Object returnValue = getUserInfoMethod.invoke(userInfoService);
            // 对方法的输入参数进行校验
            Set<ConstraintViolation<UserInfoService>> otherSet;
            otherSet = executableValidator.validateReturnValue(
                    userInfoService,
                    getUserInfoMethod,
                    returnValue
            );
    
    }
    
  • 校验构造方法

    public class UserInfoService {
    
        /**
         * 空参构造
         */
        public UserInfoService() {}
    
        /**
         * 有参构造
         * @param userInfo
         */
        public UserInfoService(UserInfo userInfo) {
    
        }
    
    }
    
    @Test
    public void test() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            // 初始化验证器
            Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
            // 校验执行器
            ExecutableValidator executableValidator = validator.forExecutables();
            // 待验证对象
            UserInfoService userInfoService = new UserInfoService();
            // 待验证构造方法
            Constructor constructor = UserInfoService.class.getConstructor(UserInfo.class);
            // 待验证构造函数
            Object[] paramObjects = new Object[]{new UserInfo()};
            // 对方法的输入参数进行校验
            Set<ConstraintViolation<UserInfoService>> otherSet;
            otherSet = executableValidator.validateConstructorParameters(
                    constructor,
                    paramObjects
            );
    
    }
    

使用 Bean Validation 校验机制,对基本数据类型进行校验,方法是在实体类属性上使用注解标识校验方式,最后在 Controller 中具体方法的形参里添加@Vlidated注解。Bean Validation 校 验有一个缺点是,我们的数据校验是在 Java 实体类里进行约束的,如果我们有多个处理器方法需要用到同一个实体类,那么定义在实体类属性上的校验规则就不好划分了,有的处理器只需要校验一个属性,而有的处理器需要校验多个属性,我们不可能为每一个处理器都创建一个实体类。解决的方法在上一篇日志里也说到,使用分组校验方式,除此之外,还可以使用 Spring 的 Validator 接口校验,它允许我们在外部指定某一对象的校验规则


标题:Validator 参数校验
作者:Hefery
地址:http://hefery.icu/articles/2022/04/14/1649923945149.html