主要是hibernate-validator依赖,可以直接引入,也可以引入spring-boot-starter-validation
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-validationartifactId>
dependency>
- MethodArgumentNotValidException:入参为实体类,参数校验失败抛出的异常
- ConstraintViolationException:入参不是实体类,参数校验失败抛出的异常
- NotReadablePropertyException:入参为实体类集合,参数校验失败抛出的异常
@RestControllerAdvice
public class CommonExceptionHandler {
/**
* 入参为实体类,参数校验失败抛出的异常MethodArgumentNotValidException拦截
*
* @param ex
* @return
*/
@ExceptionHandler({MethodArgumentNotValidException.class})
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public BaseResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
BindingResult bindingResult = ex.getBindingResult();
StringBuilder sb = new StringBuilder("校验失败:");
for (FieldError fieldError : bindingResult.getFieldErrors()) {
sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
}
String msg = sb.toString();
return BaseResponse.FAILURE(msg);
}
/**
* 入参不是实体类,参数校验失败抛出的异常ConstraintViolationException拦截
*
* @param ex
* @return
*/
@ExceptionHandler({ConstraintViolationException.class})
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public BaseResponse handleConstraintViolationException(ConstraintViolationException ex) {
return BaseResponse.FAILURE(ex.getMessage());
}
/**
* 入参为实体类集合,参数校验失败抛出的异常NotReadablePropertyException拦截,但无法获取哪个字段参数校验失败
*
* @param ex
* @return
*/
@ExceptionHandler({NotReadablePropertyException.class})
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public BaseResponse handleNotReadablePropertyException(NotReadablePropertyException ex) {
return BaseResponse.FAILURE(ex.getMessage());
}
}
根据不同场景,使用上文1小节提到的常用注解进行校验,比如@NotNull
- 【举个栗子】:入参account不能为null,长度必须在【6,20】之间
【实体类】:
@Data
public class UserDTO {
@NotNull
private Long userId;
@Length(min = 2, max = 10)
private String userName;
@Valid
private Job job;
@Valid
@NotEmpty
private List<Bank> banks;
@Data
public static class Job {
@NotNull
private Long jobId;
@Length(min = 2, max = 10)
private String jobName;
}
@Data
public static class Bank {
@Length(min = 2, max = 10)
private String bankName;
@NotNull
@Length(min = 2, max = 10)
private String position;
}
}
【接口方法】:
@PostMapping("/save")
public BaseResponse saveUser(@RequestBody @Validated UserDTO userDTO) {
// 校验通过,才会执行业务逻辑处理
return BaseResponse.SUCCESS();
}
实体类的字段加上注解,接口方法中加上 @Validated 注解即可
- 往往实体类里面还包含则其它实体类,这时再嵌套的实体类加上@Valid即可
- 嵌套的实体类正常使用注解校验,当然接口方法要加上 @Validated 注解
- 不同场景,允许嵌套实体类为空,只要不标注@NotNull即可;实体类内添加校验注解,只要嵌套实体类不为空,就会校验
- 往往实体类里面还包含则其它实体类集合,这时再嵌套的实体类加上@Valid即可
- 嵌套的实体类正常使用注解校验,当然接口方法要加上 @Validated 注解
- 参数列表下使用List去接,即便实体类添加了参数校验注解,但是不会生效
- 需要自定义一个list集合来接收参数,并使用@Delegate注解(可以不用手动重写父类方法)
- 校验失败会抛出NotReadablePropertyException异常,如果全局异常拦截,,却无法获取哪个字段参数校验失败;栗子没有拦截
public class ValidationList<E> implements List<E> {
@Delegate // @Delegate是lombok注解 ,1.18.6以上版本可支持
@Valid // 一定要加@Valid注解
public List<E> list = new ArrayList<>();
// 一定要记得重写toString方法
@Override
public String toString() {
return list.toString();
}
}
- 不同场景共用同一个实体类,而且校验规则不一样,这时可以使用分组校验
- 使用时,在对于的注解加上group即可,值为定义的类或接口都可以,如 @NotNull(groups = {Update.class})
- 接口参数列表使用的@Validated(UserDTO1.Save.class) ,也要标注用的哪个分组规则
【实体类】:
@Data
public class UserDTO1 {
@NotNull(groups = {Update.class})
private Long userId;
@Length(min = 2, max = 10, groups = {Save.class})
private String userName;
@Valid
private Job job;
@Data
public static class Job {
@NotNull(groups = {Update.class})
private Long jobId;
@Length(min = 2, max = 10, groups = {Save.class})
private String jobName;
}
/**
* 保存的时候校验分组
*/
public interface Save {
}
/**
* 更新的时候校验分组
*/
public interface Update {
}
}
【举个栗子】:添加时userId允许为空,更新时不允许为空
原理和更多内容可以查看参考链接二
【参考链接】:
·
参考一:Hibernate Validator 参数验证 单个实体类与List集合的验证
参考二:SpringBoot 实现接口的各种参数校验