Springboot 参数校验
Springboot项目中,引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
基本的使用网上有很多教程,就不介绍了。
关于使用
- 校验@RequestParam、@PathVariable或者无注解的非Java Beand等,只需在controller类上加@Validated注解,而无需在参数上添加@Valid或者@Validated注解;如不在controller上添加@Validated,则需要为每个参数添加@Valid或者@Validated。
- 校验JavaBean,必须在参数前加@Validated或者@Valid。
- @Validated支持分组校验。
- @Valid支持嵌套校验。
下面以该类为例
@Data
public class TestParam {
/**
* insert时要求为null
*/
@Null(groups = Insert.class)
/**
* update时要求不为空
*/
@NotEmpty(groups = Update.class)
private List<@NotNull Long> ids;
@EnumValue(value = Gender.class)
private String gender;
@Valid
private List<@NotNull Pet> pets;
/**
* 默认分组为default,若不继承
* Validated指定分组后,非指定分组将失效
*/
public interface Insert extends Default {
}
public interface Update extends Default {
}
}
@Data
public class Pet {
@Size(min = 1, max = 5)
private String name;
}
分组校验
分组校验有点类似Jackson序列化的JsonView,不同的情况下采取不同的策略,例如,注册时账户名必须在数据库中唯一,而登录时账户必须在数据库中存在,这个时候就需要为校验设置分组了。
情况一
@PostMapping("/insert")
public TestParam insert(@RequestBody @Validated(TestParam.Insert.class) TestParam param) {
return param;
}
情况二
@PostMapping("/update")
public TestParam update(@RequestBody @Validated(TestParam.Update.class) TestParam param) {
return param;
}
自定义校验
如下为自定义枚举值校验,用于解决属性定义不是enum类型,但想约束其值为枚举类型,例如上面的gender,假如定义为Gender,那么默认只能MALE或者FEMALE,想要男、女也通过校验,可以使用该注解
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {EnumValidator.class})
public @interface EnumValue {
String message() default "错误的枚举值";
EnumValidateBy by() default EnumValidateBy.TOSTRING;
Class<? extends Enum<?>> targetEnum();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public enum EnumValidateBy {
/**
* 使用枚举类的哪个值校验
* 如MAEL("男"), FEMALE("女")
* NAME 值必须为MAEL或者FEMALE
* TOSTRING 重写toString方法返回男或者女, 值必须为男或者女
* BOTH 以上值均可
*/
BOTH, NAME, TOSTRING
}
实现ConstraintValidator接口校验
public class EnumValidator implements ConstraintValidator<EnumValue, String> {
private final Set<String> set = new HashSet<>(8);
@Override
public void initialize(EnumValue constraintAnnotation) {
Class<? extends Enum<?>> enumCls = constraintAnnotation.targetEnum();
EnumValidateBy by = constraintAnnotation.by();
Enum<?>[] constants = enumCls.getEnumConstants();
if (by == EnumValidateBy.NAME) {
set.addAll(Arrays.stream(constants).map(Enum::name).collect(Collectors.toSet()));
} else if (by == EnumValidateBy.TOSTRING) {
set.addAll(Arrays.stream(constants).map(Enum::toString).collect(Collectors.toSet()));
} else {
set.addAll(Arrays.stream(constants).map(Enum::name).collect(Collectors.toSet()));
set.addAll(Arrays.stream(constants).map(Enum::toString).collect(Collectors.toSet()));
}
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
null不在set中,校验不通过
集合元素校验
基本类型
直接在泛型参数上加上需要的注解即可
JavaBean类型
- 如仅对整个JavaBean进行校验,例如@NotNull注解,直接将注解写在泛型参数即可
- 如需对集合中每个JavaBean的属性进行校验,还需加上@Valid注解,如上面的pets
快速失败模式
hibernate官网的配置如下:
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.addProperty( "hibernate.validator.fail_fast", "true" )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
只要往容器中注入Validator对象就行了。
因为spring自动配置好了,但没有开启快速失败模式,最后,参照Springboot自动配置的ValidationAutoConfiguration,只添加了快速失败部分,其余未做改动。
@Configuration
public class ValidationConfig {
@Bean
public Validator validatorFactory(ApplicationContext context) {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean() {
@Override
protected void postProcessConfiguration(javax.validation.Configuration<?> configuration) {
configuration.addProperty("hibernate.validator.fail_fast", "true");
}
};
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory(context);
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
return factoryBean;
}
}
总结
- 在controller层使用
- 分组时记得继承Default接口
- 集合嵌套校验
- 快速失败模式