本篇主要讲解使用,下的校验方法对实体类进行自动校验, 直接对数据进行校验,通过对接收的数据进行校验,如果不符合我们定义的要求则会提示对应的message信息,具体怎么做的,下面一步步来说明。
初步介绍校验
先看下下面的一些数据校验方法:
AssertFalse AssertTrue DecimalMax DecimalMin Digits Email Future FutureOrPresent Min Max Negative NegativeOrZero NotBlank NotEmpty NotNull Null Past PastOrPresent Pattern Positive PositiveOrZero Size
再来看下部分校验方法
Length
URL
Range
SafeHtml
具体怎么使用不是本节的重点,大家可以在需要用到的时候参考源码获取网上查阅资料,下面据两个例子来讲解:
@Length(min = 1, max = 30, message = "规则描述字段长度需要在{min}和{max}之间", groups = {, }) private String ruleDesc;@ApiModelProperty("规则优先级") @NotEmpty(message="规则优先级不可为空",groups = {,}) @Pattern(regexp = RegexpUtils.NON_ZERO_NEGATIVE_INTEGERS_REGEXP,message = "规则优先级必须为正整数") private String salience;
使用上面的校验需要引入依赖:
spring-boot-starter-web包里面有hibernate-validator包,不需要引用hibernate validator依赖。
<dependency>
<groupId></groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
如果是Spring Mvc,那可以直接添加hibernate-validator依赖
<dependency>
<groupId></groupId>
<artifactId>hibernate-validator</artifactId>
<version>7.0.</version>
</dependency>
<dependency> <groupId></groupId> <artifactId>validation-api</artifactId> </dependency>在spring-boot-starter-web包也已经被引入了
校验分组
如果需要分组,可以定义分组:
public interface UpdateGroup extends Update { }import ; public interface InsertGroup extends Insert { }
hibernate的校验模式
Hibernate Validator有以下两种验证模式:
1 、普通模式(默认是这个模式)
普通模式(会校验完所有的属性,然后返回所有的验证失败信息)
2、快速失败返回模式
快速失败返回模式(只要有一个验证失败,则返回)
默认是普通模式
配置校验模式:
@Configuration
public class ValidatorConfiguration {
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = ()
.configure()
.addProperty(".fail_fast", "true")
.buildValidatorFactory();
Validator validator = ();
return validator;
}
}
rest接口参数校验
对@PathVariable和@RequestParam参数进行校验,需要在入参声明约束的注释
@GetMapping
public Result getByUri(@RequestParam @NotEmpty String uri) {
List<GatewayRoutePO> list = (().uri(uri).build());
return ((list).get().stream().findFirst());
}
@GetMapping(value = "/get/{id}")
public Result get(@PathVariable(name = "id") @NotBlank String routId) {
("get with id:{}", routId);
return ((routId));
}
对于RequestBody的参数校验直接在实体参数前加上 Validated 或者
@PostMapping(value = "/conditions")
public Result search(@Validated @RequestBody GatewayRouteQueryParam gatewayRouteQueryParam) {
return ((gatewayRouteQueryParam));
}
校验原理
这里需要提到RequestResponseBodyMethodProcessor,这个类有两个作用,
1:用于解析@RequestBody标注的参数
2:处理ResponseBody标注方法的返回值
@Override
public boolean supportsParameter(MethodParameter parameter) {
return ();
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (((), ) ||
());
}
解析RequestBody标注参数的方法是resolveArgument
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = ();
Object arg = readWithMessageConverters(webRequest, parameter, ());
String name = (parameter);
if (binderFactory != null) {
WebDataBinder binder = (webRequest, arg, name);
if (arg != null) {
//进行参数校验
validateIfApplicable(binder, parameter);
//校验不通过抛MethodArgumentNotValidException异常
if (().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, ());
}
}
if (mavContainer != null) {
(BindingResult.MODEL_KEY_PREFIX + name, ());
}
}
return adaptArgumentIfNecessary(arg, parameter);
}
校验方法:AbstractMessageConverterMethodArgumentResolver#validateIfApplicable 获取Validated注解,根据注解参数和注解信息验证DataBinder#validate
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
Annotation[] annotations = ();
for (Annotation ann : annotations) {
Validated validatedAnn = (ann, );
if (validatedAnn != null || ().getSimpleName().startsWith("Valid")) {
Object hints = (validatedAnn != null ? () : (ann));
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
(validationHints);
break;
}
}
}
public void validate(Object... validationHints) {
Object target = getTarget();
(target != null, "No target to validate");
BindingResult bindingResult = getBindingResult();
// Call each validator with the same binding result
for (Validator validator : getValidators()) {
if (!(validationHints) && validator instanceof SmartValidator) {
((SmartValidator) validator).validate(target, bindingResult, validationHints);
}
else if (validator != null) {
(target, bindingResult);
}
}
}
通过在参数上加注释校验参数是否符合条件, 实际上就是使用AOP拦截,Spring通过MethodValidationPostProcessor动态主从AOP切面,然后使用MethodValidationInterceptor对切点进行织入增强
public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor
implements InitializingBean {
@Override
public void afterPropertiesSet() {
//创建切面
Pointcut pointcut = new AnnotationMatchingPointcut(, true);
//创建advisor进行增强
= new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice());
}
}
自定义校验:
定义自定义约束,有三个步骤
- 创建约束注解
- 实现一个验证器
- 定义默认的错误信息
自定义约束注解:
@Documented
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Constraint(validatedBy = {})
@Retention(RUNTIME)
@Repeatable()
public @interface Mobile {
/**
* 错误提示信息,可以写死,也可以填写国际化的key
*/
String message() default "手机号码不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String regexp() default "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$";
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
@interface List {
Mobile[] value();
}
}
public class MobileValidator implements ConstraintValidator<Mobile, String> {
/**
* 手机验证规则
*/
private Pattern pattern;
@Override
public void initialize(Mobile mobile) {
pattern = (());
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
return (value).matches();
}
}
测试自定义校验:
import ;
import ;
import ;
import ;
import ;
import ;
public class ValidatorUtil {
private static final Validator validator= ().getValidator();
public static <T> String validate(T object){
Set<ConstraintViolation<T>> violationSet=(object);
if(violationSet!=null&&!(violationSet)){
Iterator<ConstraintViolation<T>> iterator=();
StringBuffer sb=new StringBuffer(64);
while(()){
(().getMessage()).append(";");
}
return ();
}
return null;
}
}
配置全局校验
/**
* 全局参数验证
*/
@Slf4j
@Order(1)
@ControllerAdvice
@RestController
public class ValidationException {
@ExceptionHandler(value = )
public CommonResult ValidExceptionHandler(MethodArgumentNotValidException exception) throws Exception {
BindingResult bindingResult = ();
if (()) {
String defaultMessage = ().getDefaultMessage();
return (defaultMessage);
}
return ("校验通过");
}
}
在接口中使用数据校验:
@PostMapping("/save") @ApiOperation(value = "保存规则数据") public CommonResult saveCvbRule(@Validated() @RequestBody Rule rule)
如果校验不通过会向前端返回message校验失败信息
参考资料:基于springboot使用hibernate validator校验数据
hibernate-validation官网