参数校验
介绍
@Validated与@Valid的区别
- @Validated注解是spring提供的,提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制。没有添加分组属性时,默认验证没有分组的验证属性(Default分组);
- @Validated:可以用在类型、方法和方法参数上,但是不能用在成员属性(字段上;
- @Validated: 用在方法入参上无法单独提供嵌套验证功能,也无法提示框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。
- @Valid:作为标准JSR-303规范,还没有吸收分组的功能;
- @Valid:可以用在方法、方法参数、构造函数、方法参数和成员属性(字段)上;
- @Valid加在方法参数时并不能够自动进行嵌套验证,而是用在需要嵌套验证类的相应字段上,来配合方法参数上@Validated或@Valid来进行嵌套验证。
注解说明
Hibernate Validator两种校验模式——普通&快速
-
普通模式(会校验完所有的属性,然后返回所有的验证失败信息,默认是这个模式)
-
快速失败返回模式(只要有一个字段验证失败,就返回结果)
在@Configuration Class中配置以下代码,将Validator设置为快速失败返回模式
@Bean
public Validator validator(){
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class)
.configure()
.addProperty( "hibernate.validator.fail_fast", "true" )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
使用
引入依赖
由于spring-boot-starter-web(springboot 2.3以下版本)依赖默认集成了Hibernate Validator,所以无需添加任何依赖和相关配置,只需要在项目中引入spring-boot-starter-web依赖即可(演示springboot版本为2.5.4)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
全局异常处理器
@RestControllerAdvice
@Slfj
@AutoConfigurationPackage
public class GlobalExceptionHandler {
//spring-context包里面的异常
//实体对象前不加@RequestBody注解,单个对象内属性校验未通过抛出的异常类型
@ExceptionHandler(BindingException.class)
public ResponseEntity<ExceptionResponseVO> methodArguments(BindingException e){
log.warn("throw BindingException,{}",e);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ExceptionResponseVO.error(NEError.INVALID_PARAMETER, e.getBindingResult().getFieldError().getDefaultMessage()));
}
//实体对象前不加@RequestBody注解,校验方法参数或方法返回值时,未校验通过时抛出的异常
//Validation-api包里面的异常
@ExceptionHandler(ValidationException.class)
public ResponseEntity<ExceptionResponseVO> methodArguments(ValidationException e){
log.warn("throw ValidationException,{}",e);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ExceptionResponseVO.error(NEError.INVALID_PARAMETER,e.getCause().getMessage()));
}
//spring-context包里面的异常,实体对象前加@RequestBody注解,抛出的异常为该类异常
//方法参数如果带有@RequestBody注解,那么spring mvc会使用RequestResponseBodyMethodProcessor
//对参数进行序列化,并对参数做校验
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ExceptionResponseVO> methodArguments(MethodArgumentNotValidException e){
log.warn("throw MethodArgumentNotValidException,{}",e);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ExceptionResponseVO.error(NEError.INVALID_PARAMETER, e.getBindingResult().getFieldError().getDefaultMessage()));
}
@ExceptionHandler(Exception.class)
public ResponseEntity methodArguments(Exception e){
log.warn("throw exception,{}",e);
return ResponseEntity.badRequest().body(e.getMessage());
}
}
自定义参数检验
需求:我需要根据传入多个参数,有一部分参数校验条件取决于其他参数的输入,比如我只需要当name参数等于“testUser”时,校验age是否大于20,当name=“user”,校验age是否大于30,没有值则不校验
1.分组
根据校验逻辑分组
@Data
@NoArgsConstructor
@AllArgsConstructor
//指定组序列化提供器
@GroupSequenceProvider(value = AccountGroupSeqProvider.class)
public class AccountConfigParam {
/**
* 账号名称
*/
@NotBlank(message = "账号名称不可为空")
private String username;
/**
* 账号id
*/
@NotBlank(message = "账号id不可为空", groups = {WeiBoGroup.class, WeiXinGroup.class})
private String userid;
public interface FirstGroup{}
public interface SecondGroup {
}
2. 自定义分组序列化提供器
public class AccountGroupSeqProvider implements DefaultGroupSequenceProvider<AccountConfigParam> {
@Override
public List<Class<?>> getValidationGroups(AccountConfigParam param) {
List<Class<?>> defaultGroupSequency = new ArrayList<>();
//这一步不能省略,否则会抛错
defaultGroupSequency.add(AccountConfigParam.class);
if (param!=null){
if ("user".equals(param.getMedium())){
defaultGroupSequency.add(AccountConfigParam.WeiBoGroup.class);
}
}
return defaultGroupSequency;
}
}