https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#validation

https://bestzuo.cn/posts/spring-validation.html

spring 框架实现了一套validation,底层采用hibernate-validator。

1 maven 坐标

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>

2 客户端参数校验

2.1 表单

class TestVO {
    @NotNull
    private String name;

    @NotNull
    private String password;

    @NotNull
    private String email;

    // getter setter
}

2.2 使用 @Validated

方式一:

@PostMapping("/validation")
public boolean learnValidator(@Validated TestVO vo) {
    return true;
}

此方式TestVO如果不符合注解标注的,会抛出异常。

方式二:

@PostMapping("/validation")
public boolean learnValidator(@Validated TestVO vo, BindingResult result) {
    // result 异常自己可以处理。
    return true;
}

此方式不会抛出异常,会将异常信息存放在BindResult中,可以对BindResult进行解析,进行异常处理。

2.3 将异常统一处理

@RestControllerAdvice
public class GlobalControllerAdvice {
    private static final String BAD_REQUEST_MSG = "客户端请求参数错误";
    // <1> 处理 form data方式调用接口校验失败抛出的异常
    @ExceptionHandler(BindException.class)
    public ResultInfo bindExceptionHandler(BindException e) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        List<String> collect = fieldErrors.stream()
                .map(o -> o.getDefaultMessage())
                .collect(Collectors.toList());
        return new ResultInfo().success(HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_MSG, collect);
    }
    // <2> 处理 json 请求体调用接口校验失败抛出的异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultInfo methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        List<String> collect = fieldErrors.stream()
                .map(o -> o.getDefaultMessage())
                .collect(Collectors.toList());
        return new ResultInfo().success(HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_MSG, collect);
    }
    // <3> 处理单个参数校验失败抛出的异常
    @ExceptionHandler(ConstraintViolationException.class)
    public ResultInfo constraintViolationExceptionHandler(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        List<String> collect = constraintViolations.stream()
                .map(o -> o.getMessage())
                .collect(Collectors.toList());
        return new ResultInfo().success(HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_MSG, collect);
    }
}

2.4 分组校验

如果同一个参数,需要在不同场景下应用不同的校验规则,就需要用到分组校验了。比如:新注册用户还没起名字,我们允许 name 字段为空,但是不允许将名字更新为空字符。

分组校验有三个步骤:

  • 定义一个分组类(或接口)

  • 在校验注解上添加groups属性指定分组

  • Controller 方法的 @Validated 注解添加分组类

public interface Update extends Default{
}

public class UserVO {
    @NotBlank(message = "name 不能为空",groups = Update.class)
    private String name;
    // 省略其他代码...
}

@PostMapping("update")
public ResultInfo update(@Validated({Update.class}) UserVO userVO) {
    return new ResultInfo().success(userVO);
}

所有不加分组标识的,都默认是Default.class。 如果校验的实体里还有对象,可以加@Valid,进行递归校验。