likes
comments
collection
share

SpringBoot + validator参数校验

作者站长头像
站长
· 阅读数 16

@Validated和@Valid的区别主要体现在3个方面:

@Valid@Validated都是用来校验接收参数的。
 
@Valid是使用Hibernate validation的时候使用
 
@Validated是只用Spring Validator校验机制使用
 
说明:java的JSR303声明了@Valid这类接口,而Hibernate-validator对其进行了实现。
 
 
 
@Validated@Valid区别:
 
@Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上,不支持嵌套检测
@Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上,支持嵌套检测
 
 
 
注意:SpringBoot使用@Valid注解需要引入如下POM
 
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
  1. 分组 @Validated:提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制。没有添加分组属性时,默认验证没有分组的验证属性。 SpringBoot + validator参数校验

SpringBoot + validator参数校验

@Valid:作为标准JSR-303规范,还没有吸收分组的功能。

  1. 注解地方

@Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上

@Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上

两者是否能用于成员属性(字段)上直接影响能否提供嵌套验证的功能。

  1. 嵌套验证

@Validated:用在方法入参上无法单独提供嵌套验证功能。不能用在成员属性(字段)上,也无法提示框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

@Valid:用在方法入参上无法单独提供嵌套验证功能。能够用在成员属性(字段)上,提示验证框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

SpringBoot + validator参数校验

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参数必须在合适的范围内

快速开始

在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'

封装统一异常处理类:

@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));
    }
}

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

路径参数校验

我们在使用restful定义接口时,可能会使用@PathVariable来直接在接口url后传递参数,有时候传递的参数并不符合我们定义的参数规则,此时我们可以进行参数校验。

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

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

定义传递的userId必须是由数字组成。

通过url进行参数校验,我们只看到了报错,并没有提示具体错误信息,也不能像使用注解那样自定义错误信息返回。

分组校验

在我们日常开发中常常涉及到更新,在更新的时候往往需要传递数据的Id,但是新增的时候又不需要这个Id,此时我们可以采用分组来进行数据Id的校验。

分组定义

@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参数校验

自定义校验

在参数校验时,可能Hibernate Validate自带的校验规则并不能满足我们业务需求,此时我们可以实现自定义校验规则。

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

定义注解

@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参数校验

2.5.2 校验实现类

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中添加校验注解:

@Data
public class Employee {
    @NotBlank(message = "请填写雇员名称")
    private String name;

    @IdCode(message = "请填写正确的身份证信息!")
    private String idCode;
}

输出:

SpringBoot + validator参数校验

输入了错误的身份证,则校验不通过,提示定义的信息。

在日常工作中,使用Hibernate Validate进行参数校验真的会事半功倍,同时也提供了自定义校验的实现,让我们更加专注于业务。

转载自:https://juejin.cn/post/7268173090915172406
评论
请登录