引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
快速失败配置
@Configuration
public class ValidatorConf {
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.failFast( true )
.buildValidatorFactory();
return validatorFactory.getValidator();
}
}
单个参数校验:
@RestController
@Validated
public class PingController {
@GetMapping("/getUser")
public String getUserStr(@NotNull(message = "name 不能为空") String name,
@Max(value = 99, message = "不能大于99岁") Integer age) {
return "name: " + name + " ,age:" + age;
}
}
实体对象校验
@PostMapping("/test2")
public SysUser test2(@RequestBody @Valid SysUser sysUser) {
SysUser sysUser1 = new SysUser();
BeanUtils.copyProperties(sysUser, sysUser1);
sysUser1.setCreateTime(LocalDateTime.now());
return sysUser1;
}
public class SysUser {
@Null
@JsonAlias("user-Id")
private Integer userId;
@NotNull
@Length(min = 10, max = 20, message = "长度最小10位,最长20位")
private String userName;
@Positive
private Integer age;
@PastOrPresent
private LocalDateTime createTime;
}
级联验证
public class SysUser {
@Null
@JsonAlias("user-Id")
private Integer userId;
@NotNull
@Length(min = 10, max = 20, message = "长度最小10位,最长20位")
private String userName;
@Positive
private Integer age;
@PastOrPresent
private LocalDateTime createTime;
@Valid //级联验证
private Department department;
}
public class Department {
private Integer id;
@NotNull
@Length(min = 3,max = 10,message = "长度大于3 小于10")
private String name;
@Valid //级联验证
private List<SysUser> sysUsers;
}
分组验证
自定义分组接口
public interface Add extends Default {
}
public interface Update extends Default{
}
将自定义分组接口应用到实体类上
public class SysUser {
@Null(groups = Add.class)
@NotNull(groups = Update.class)
private Integer userId;
}
在Controller中进行校验
@PostMapping("/add")
public SysUser add(@RequestBody @Validated(Add.class) SysUser sysUser) {
SysUser sysUser1 = new SysUser();
BeanUtils.copyProperties(sysUser, sysUser1);
sysUser1.setCreateTime(LocalDateTime.now());
return sysUser1;
}
@PostMapping("/update")
public SysUser update(@RequestBody @Validated(Update.class) SysUser sysUser) {
SysUser sysUser1 = new SysUser();
BeanUtils.copyProperties(sysUser, sysUser1);
sysUser1.setCreateTime(LocalDateTime.now());
return sysUser1;
}
自定义注解验证
list中做分组校验
不使用分组
@RestController
@RequestMapping("/sysUser")
@Validated
public class SysUserController {
@PostMapping("/batchProcess")
public List<SysUser> batchProcess(@RequestBody @Valid List<SysUser> sysUser) {
return sysUser;
}
}
使用分组
自定义注解
/**
* 支持 list 中的分组校验
*/
@Target({FIELD,PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {ValidListValidator.class})
public @interface ValidList {
/**
* 要验证的分组
*/
Class<?>[] groupings() default {Default.class};
boolean quickFail() default false;
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
自定义校验器
public class ValidListValidator implements ConstraintValidator<ValidList, List> {
Class<?>[] groupings;
boolean quickFail;
@Override
public void initialize(ValidList validList) {
groupings = validList.groupings();
quickFail = validList.quickFail();
}
@Override
public boolean isValid(List list, ConstraintValidatorContext context) {
Map<Integer, Set<ConstraintViolation<Object>>> errors = new HashMap<>();
for (int i = 0; i < list.size(); i++) {
Object object = list.get(i);
Set<ConstraintViolation<Object>> error = ValidatorUtils.validator.validate(object, groupings);
if (error.size()>0) {
errors.put(i, error);
if (quickFail){
throw new ListValidException(errors);
}
}
}
if (errors.size()>0){
throw new ListValidException(errors);
}
return true;
}
}
工具类
@Component
public class ValidatorUtils {
public static Validator validator;
@Autowired
public void setValidator(Validator validator) {
ValidatorUtils.validator = validator;
}
}
使用自定义注解校验List参数
@RestController
@RequestMapping("/sysUser")
@Validated
public class SysUserController {
@PostMapping("/batchProcessAddGroup")
public List<SysUser> batchProcessAddGroup(@RequestBody @ValidList(groupings = {Add.class}) List<SysUser> sysUser) {
return sysUser;
}
@PostMapping("/batchProcessUpdateGroup")
public List<SysUser> batchProcessUpdateGroup(@RequestBody @ValidList(groupings = {Update.class}) List<SysUser> sysUser) {
return sysUser;
}
bean参数间逻辑校验
需求:身份证号码倒数第二位是奇数,性别是男,身份证号码倒数第二位是偶数,性别是女
@GroupSequenceProvider(value = SysUserGroupSequenceProvider.class)
public class SysUser {
public interface Women { }
public interface Man { }
@Null(groups = Add.class)
@NotNull(groups = Update.class)
private Integer userId;
//身份证号码
private String certCode;
//性别
@Pattern(regexp = "^\u7537$",groups = Man.class) //男
@Pattern(regexp = "^\u5973$",groups = Women.class) //女
private String sex;
}
public class SysUserGroupSequenceProvider implements DefaultGroupSequenceProvider<SysUser> {
/**
* 举例
* 1、SysUser 的身份证号码倒数第二位是奇数,性别必须是男
* 2、SysUser 的身份证号码倒数第二位是偶数,性别必须是女
*/
@Override
public List<Class<?>> getValidationGroups(SysUser sysUser) {
List<Class<?>> defaultGroupSequence = new ArrayList<>();
defaultGroupSequence.add(SysUser.class);// 相当于添加了默认组 javax.validation.groups.Default
if(sysUser == null){
return defaultGroupSequence;
}
if(StrUtil.isBlank(sysUser.getCertCode())){
return defaultGroupSequence;
}
String certCode = sysUser.getCertCode();
int second = certCode.charAt(certCode.length()-2);
if(second % 2 == 0){
//身份证号码倒数第二位是偶数 性别 女
defaultGroupSequence.add(SysUser.Women.class);
}
if(second % 2 == 1){
//身份证号码倒数第二位是奇数 性别 男
defaultGroupSequence.add(SysUser.Man.class);
}
return defaultGroupSequence;
}
}
统一异常处理
@RestControllerAdvice
@Slf4j
public class DefualtExceptionHandlerConfig {
@ExceptionHandler(value = {MethodArgumentNotValidException.class})
public ResponseEntity<ServerResponseEntity<Map<String, String>>> methodArgValidExceptionHandler(MethodArgumentNotValidException e) {
log.error("参数校验异常:", e);
Map<String, String> collect = e.getBindingResult().getFieldErrors().stream()
.collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
return ResponseEntity.status(HttpStatus.OK)
.body(ServerResponseEntity.fail(ResponseEnum.METHOD_ARGUMENT_NOT_VALID, collect));
}
@ExceptionHandler
public ResponseEntity<ServerResponseEntity<Map<Path, String>>> exceptionHandler(ConstraintViolationException e) {
log.error("参数校验异常:", e);
Map<Path, String> collect = e.getConstraintViolations().stream()
.collect(Collectors.toMap(ConstraintViolation::getPropertyPath, ConstraintViolation::getMessage));
return ResponseEntity.status(HttpStatus.OK).body(ServerResponseEntity.fail(ResponseEnum.METHOD_ARGUMENT_NOT_VALID, collect));
}
@ExceptionHandler
public ResponseEntity<ServerResponseEntity<Map<Integer, Map<Path, String>>>> exceptionHandler(ValidationException e){
log.error("参数校验异常:", e);
Map<Integer, Map<Path, String>> map = new HashMap<>();
((ListValidException)e.getCause()).getErrors().forEach((integer, constraintViolations) -> {
map.put(integer, constraintViolations.stream()
.collect(Collectors.toMap(ConstraintViolation::getPropertyPath, ConstraintViolation::getMessage)));
});
return ResponseEntity.status(HttpStatus.OK).body(ServerResponseEntity.fail(ResponseEnum.METHOD_ARGUMENT_NOT_VALID, map));
}
}
@Valid //标注在参数,属性上
@Validated //标注在bean组件上,如controller 或service ;也可以标注在接口,所有实现类都会被校验
@NotNull
@Null
//时间
@FutureOrPresent
@Past
@Future
@PastOrPresent
//字符串
@NotBlank
@NotEmpty
@Length
@URL
//字符串或数字
@Range
//数字
@Max
@Min
@Negative
@NegativeOrZero
@Positive
@PositiveOrZero
//布尔值
@AssertFalse
@AssertTrue
//浮点数
@DecimalMax
@DecimalMin
@Digits
@Email
//正则
@Pattern
//数组,集合
@Size
@ConvertGroup
参考链接:5、分组验证哔哩哔哩bilibili