你还在手动对数据进行校验,快来使用validation吧

时间:2025-03-11 07:45:32

       本篇主要讲解使用,下的校验方法对实体类进行自动校验, 直接对数据进行校验,通过对接收的数据进行校验,如果不符合我们定义的要求则会提示对应的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官网