Hi,大家好,我是抢老婆酸奶的小肥仔。

在咱们日常开发中,参数校验必不可少,一般情况下咱们习惯选用if-else来进行校验,成果便是满屏的if-else。代码既不高雅,也会令人眼花缭乱,维护也是繁琐。那么有没有高雅的参数校验呢?有,运用hibernate-validator吧,它会让你爱不释手。

废话不多说,撸起来吧。

1、简介

Hibernate Validate是Bean Validation完成的,内置了JSR303/JSR380中所有的constraint的完成,一起也额外供给了许多自界说的constraint。Bean Validation为JavaBean的验证供给了许多相关的元数据模型和API。

参数阐明

在Hibernate Validate供给了许多注解,以完成对应参数的校验。

参数 描绘
@Null 参数有必要为null
@NotNull 参数不能为null
@NotBlank 参数不能为null和空值,一般作用在字符串类型
@AssertTrue 参数有必要为true
@AssertFalse 参数有必要为false
@Min(value) 参数有必要是数字,且大于等于指定的最小值
@Max(value) 参数有必要是数字,且小于等于指定的最大值
@DecimalMin(value) 参数有必要是数字,且大于等于指定的最小值
@DecimalMax(value) 参数有必要是数字,且小于等于指定的最大值
@Length(min,max) 指定字符长度,最小值是多少,最大值是多少
@Size(max,min) 参数的巨细有必要在指定的范围
@Digits(Integer,fraction) 参数有必要是个数字,且巨细有必要在可接受范围内
@Past 参数有必要是一个过去的日期
@Future 参数有必要是一个将来的日期
@Pattern(value) 参数有必要契合指定的正则表达式
@Email 参数有必要契合电子邮箱地址
@NotEmpty 参数有必要非空
@Range 参数有必要在合适的范围内

2、快速开端

SpringBoot中运用Hibernate Validate特别简略,只需求引进jar包即可。

mavan引进:

<dependency>
  <groupId>org.hibernate.validator</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>6.2.3.Final</version>
</dependency>

gradle引进:

implementation group: 'org.hibernate.validator', name: 'hibernate-validator', version: '6.2.3.Final'

封装一致反常处理类:

/**
* @author: jiangjs
* @description: 一致反常处理办法
* @date: 2022/6/21 11:28
**/
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler({ConstraintViolationException.class, MethodArgumentNotValidException.class})
    public ResultUtil<String> resolveViolationException(Exception ex){
        StringJoiner messages = new StringJoiner(",");
        if (ex instanceof ConstraintViolationException){
            Set<ConstraintViolation<?>> violations = ((ConstraintViolationException) ex).getConstraintViolations();
            for (ConstraintViolation<?> violation : violations) {
                messages.add(violation.getMessage());
            }
        } else {
            List<ObjectError> allErrors = ((MethodArgumentNotValidException) ex).getBindingResult().getAllErrors();
            for (ObjectError error : allErrors) {
                messages.add(error.getDefaultMessage());
            }
        }
        return ResultUtil.error(400,String.valueOf(messages));
    }
}

2.1 参数校验

2.1.1 创建实体

/**
 * @author: jiangjs
 * @description:
 * @date: 2022/6/21 10:46
 **/
@Data
public class UserInfo {
    private Long userId;
    @NotBlank(message = "用户名不能为空")
    private String userName;
    @NotBlank(message = "暗码不能为空")
    private String password;
    @Email(message = "请填写正确的email")
    @NotBlank(message = "email不能为空")
    private String email;
}

上述实体中,咱们看到运用了@NutBlank,@Email来校验String参数不能null和空,一起也校验了email是否契合email规矩。

2.1.2 测验

运用Hibernate Validate则直接在参数前运用 @Validated注解即可。

@PostMapping("/insertUserInfo.do")
public ResultUtil<?> insertUserInfo(@Validated @RequestBody UserInfo userInfo){
    return ResultUtil.success(userInfo.toString());
}

输出:

在参数校验时,会根据参数的次序来进行校验,当一个参数不契合规矩时,则直接回来校验成果。

  1. 咱们在进行参数传递时,不填写username。

SpringBoot + validator优雅参数校验,消除if-else

  1. 校验email是否契合规矩

SpringBoot + validator优雅参数校验,消除if-else

2.2 路径参数校验

咱们在运用restful界说接口时,或许会运用@PathVariable来直接在接口url后传递参数,有时分传递的参数并不契合咱们界说的参数规矩,此刻咱们能够进行参数校验。

校验规矩:参数:正则表达式

@GetMapping("/getUserInfoByUserId.do/{userId:[0-9_]+}")
public String getUserInfoByUserId(@PathVariable("userId") Integer userId){
    return String.valueOf(userId);
}

界说传递的userId有必要是由数字组成。

输出:

当传递参数不契合正则时,则直接报错。

SpringBoot + validator优雅参数校验,消除if-else

经过url进行参数校验,咱们只看到了报错,并没有提示具体过错信息,也不能像运用注解那样自界说过错信息回来。

2.3 分组校验

在咱们日常开发中常常涉及到更新,在更新的时分往往需求传递数据的Id,可是新增的时分又不需求这个Id,此刻咱们能够选用分组来进行数据Id的校验。

2.3.1 分组界说

/**
 * @author: jiangjs
 * @description:
 * @date: 2022/6/21 10:46
 **/
@Data
public class UserInfo {
    public interface Update{};
    @NotNull(message = "userId不能为空",groups = Update.class)
    private Long userId;
    @NotBlank(message = "用户名不能为空")
    private String userName;
    @NotBlank(message = "暗码不能为空")
    private String password;
    @Email(message = "请填写正确的email")
    @NotBlank(message = "email不能为空")
    private String email;
}

咱们在实体类中界说了一个接口Update,然后在注解中的groups中指定即可。

2.3.2 测验

/**
     * 分组校验数据信息:界说Update
     */
    @PostMapping("/updateUserInfo.do")
    public ResultUtil<?> updateUserInfo(@Validated(value = UserInfo.Update.class) @RequestBody UserInfo userInfo){
        return ResultUtil.success(userInfo.toString());
    }

在@Validated中value指定实体中界说的接口。

输出:

SpringBoot + validator优雅参数校验,消除if-else

2.4 校验传递

在日常开发中咱们经常会在实体中引进其他实体,并且又要对其他实体中的数据进行数据校验,此刻就存在查验传递,一般咱们在需求校验的实体前运用 @Valid即可。

2.4.1 界说实体

增加一个雇员信息实体:

/**
 * @author: jiangjs
 * @description:
 * @date: 2022/6/21 14:48
 **/
@Data
public class Employee {
    @NotBlank(message = "请填写雇员称号")
    private String name;
}

在userInfo实体中引进Employee

/**
 * @author: jiangjs
 * @description:
 * @date: 2022/6/21 10:46
 **/
@Data
public class UserInfo {
    public interface Update{};
    @NotNull(message = "userId不能为空",groups = Update.class)
    private Long userId;
    @NotBlank(message = "用户名不能为空")
    private String userName;
    @NotBlank(message = "暗码不能为空")
    private String password;
    @Email(message = "请填写正确的email")
    @NotBlank(message = "email不能为空")
    private String email;
    @Valid
    @NotNull(message = "雇员信息不能为空")
    private Employee employee;
}

2.4.2 测验

@PostMapping("/insertUserInfo.do")
public ResultUtil<?> insertUserInfo(@Validated @RequestBody UserInfo userInfo){
    return ResultUtil.success(userInfo.toString());
}

输出:

SpringBoot + validator优雅参数校验,消除if-else

2.5 自界说校验

在参数校验时,或许Hibernate Validate自带的校验规矩并不能满意咱们事务需求,此刻咱们能够完成自界说校验规矩。

需求:对身份证进行校验。

2.5.1 界说注解

/**
 * @author: jiangjs
 * @description: 身份证校验规矩
 * @date: 2022/6/21 11:28
 **/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER,ElementType.FIELD})
@Constraint(validatedBy = IdCodeValidator.class)
public @interface IdCode {
    String message() default "请填写正确的身份证信息";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

@Constraint:Hibernate Validate供给的注解,用于完成自界说校验规矩。其间validatedBy便是指定进行参数校验的完成类。

注:在自界说校验注解时有必要带上groups()和payload(),不然会报错。

SpringBoot + validator优雅参数校验,消除if-else

2.5.2 校验完成类

/**
 * @author: jiangjs
 * @description: 身份证校验
 * @date: 2022/6/21 14:25
 **/
public class IdCodeValidator implements ConstraintValidator<IdCode,Object> {
        /**
         * 18位二代身份证号码的正则表达式
         */
    public static final String REGEX_ID_NO_18 = "^"
                // 6位地区码
            + "\d{6}"
                // 年YYYY
            + "(18|19|([23]\d))\d{2}"
                // 月MM
            + "((0[1-9])|(10|11|12))"
                // 日DD
            + "(([0-2][1-9])|10|20|30|31)"
                // 3位次序码
            + "\d{3}"
                // 校验码
            + "[0-9Xx]"
            + "$";
    /**
     * 15位一代身份证号码的正则表达式
     */
    public static final String REGEX_ID_NO_15 = "^"
            + "\d{6}" // 6位地区码
            + "\d{2}" // 年YYYY
            + "((0[1-9])|(10|11|12))" // 月MM
            + "(([0-2][1-9])|10|20|30|31)" // 日DD
            + "\d{3}"// 3位次序码
            + "$";
    @Override
    public void initialize(IdCode constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }
    @Override
    public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
        String idCode = String.valueOf(o);
        if (idCode.length() != 15 && idCode.length() != 18){
           return false;
       }
        return idCode.matches(REGEX_ID_NO_18) || idCode.matches(REGEX_ID_NO_15);
    }
}

ConstraintValidator:Hibernate Validate供给的接口,ConstraintValidator<A extends Annotation, T>,第一个参数有必要是注解,即Annotation,第二个参数是泛型。一起在接口中供给了两个办法:

  • initialize(A constraintAnnotation),初始化注解,
  • boolean isValid(T var1, ConstraintValidatorContext var2):校验参数,true表示校验经过,false表示校验失败。

2.5.3 测验

/**
 * 自界说校验数据信息
 */
@PostMapping("/definedEmployeeInfo.do")
public ResultUtil<?> definedEmployeeInfo(@Validated @RequestBody Employee employee){
    return ResultUtil.success(employee.toString());
}

在Employee中增加校验注解:

/**
 * @author: jiangjs
 * @description:
 * @date: 2022/6/21 14:48
 **/
@Data
public class Employee {
    @NotBlank(message = "请填写雇员称号")
    private String name;
    @IdCode(message = "请填写正确的身份证信息!")
    private String idCode;
}

输出:

SpringBoot + validator优雅参数校验,消除if-else

输入了过错的身份证,则校验不经过,提示界说的信息。

在日常工作中,运用Hibernate Validate进行参数校验真的会事半功倍,一起也供给了自界说校验的完成,让咱们更加专心于事务。

好了,今天的分享就先到这,谢谢大家听我唠叨。