springboot参数校验

mave依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

注解使用场景

一、注解修饰方法的参数

eg.
public void a(@NotEmpty String userName){

}
这一类的情况,需要在类上或者方法上添加@Valid或者@Validated注解

二、注解修饰参数实体的属性

class ValidVo{
	@NotNull
	String name;
}

eg.
public void a(@Valid ValidVo userName){

}
这一类的情况,需要在方法参数添加@Valid或者@Validated注解

注意事项

校验参数的注解指针对controller层的方法有效,对service层的方法无效;

常见的约束注解如下:

注解 功能
@AssertFalse 可以为null,如果不为null的话必须为false
@AssertTrue 可以为null,如果不为null的话必须为true
@DecimalMax 设置不能超过最大值
@DecimalMin 设置不能超过最小值
@Digits 设置必须是数字且数字整数的位数和小数的位数必须在指定范围内
@Future 日期必须在当前日期的未来
@Past 日期必须在当前日期的过去
@Max 最大不得超过此最大值
@Min 最大不得小于此最小值
@NotNull 不能为null,可以是空
@Null 必须为null
@Pattern 必须满足指定的正则表达式
@Size 集合、数组、map等的size()值必须在指定范围内
@Email 必须是email格式
@Length 长度必须在指定范围内
@NotBlank 字符串不能为null,字符串trim()后也不能等于“”
@NotEmpty 不能为null,集合、数组、map等size()不能为0;字符串trim()后可以等于“”
@Range 值必须在指定范围内
@URL 必须是一个URL

springboot校验参数源码

对于第一种情况参数校验的逻辑由MethodValidationInterceptor进行校验

public class MethodValidationInterceptor implements MethodInterceptor {
		...
		@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// Avoid Validator invocation on FactoryBean.getObjectType/isSingleton
		if (isFactoryBeanMetadataMethod(invocation.getMethod())) {
			return invocation.proceed();
		}

		Class<?>[] groups = determineValidationGroups(invocation);

		// Standard Bean Validation 1.1 API
		ExecutableValidator execVal = this.validator.forExecutables();
		Method methodToValidate = invocation.getMethod();
		Set<ConstraintViolation<Object>> result;

		try {
			result = execVal.validateParameters(
					invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
		}
		catch (IllegalArgumentException ex) {
			// Probably a generic type mismatch between interface and impl as reported in SPR-12237 / HV-1011
			// Let's try to find the bridged method on the implementation class...
			methodToValidate = BridgeMethodResolver.findBridgedMethod(
					ClassUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass()));
			result = execVal.validateParameters(
					invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
		}
		if (!result.isEmpty()) {
			throw new ConstraintViolationException(result);
		}

		Object returnValue = invocation.proceed();

		result = execVal.validateReturnValue(invocation.getThis(), methodToValidate, returnValue, groups);
		if (!result.isEmpty()) {
			throw new ConstraintViolationException(result);
		}

		return returnValue;
	}
	    ...
}

主要代码
ExecutableValidator execVal = this.validator.forExecutables();
execVal.validateParameters(
invocation.getThis(), methodToValidate, invocation.getArguments(), groups);

第二种情况会在WebDataBind的过程由ModelAttributeMethodProcessor进行参数校验

单独使用Validator校验参数

 Validator  validator = Validation.byDefaultProvider().configure()
                .messageInterpolator(new ResourceBundleMessageInterpolator(
                        new PlatformResourceBundleLocator("validationMessages"))) //手动指定校验提示资源(默认在resource目录下ValidationMessages.properties)
                .buildValidatorFactory().getValidator();
             
       Set<ConstraintViolation<Object>> constraintViolations = smartValidator.forExecutables().validateParameters(joinPoint.getTarget(), signature.getMethod(), args);

注解必须使用JSR的注解,spring的Validated注解不会生效

springboot中使用aop校验非controller层参数

注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ValidParam {

    Class<?>[] value() default {};
}

切面逻辑

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintViolation;
import javax.validation.ValidationException;
import javax.validation.Validator;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;

@Aspect
@Component
@Slf4j
public class ParamsCheckAspect {

    final Validator smartValidator;

    public ParamsCheckAspect(Validator smartValidator) {
        this.smartValidator = smartValidator;
    }

    @Before("@within(validParam)||@annotation(validParam)")
    public void before(JoinPoint joinPoint, ValidParam validParam) {

        log.debug("校验参数 {} ,{} ", joinPoint, validParam);
        Object[] args = joinPoint.getArgs();
        if (args.length == 0)
            return;

        Class<?>[] groups = new Class[0];
        if (validParam != null)
            groups = validParam.value();
        log.debug("参数分组 {} ", Arrays.deepToString(groups));
        Set<ConstraintViolation<Object>> constraintViolations;
        constraintViolations = smartValidator.forExecutables().validateParameters(joinPoint.getTarget(), ((MethodSignature) joinPoint.getSignature()).getMethod(), args, groups);
        log.debug("校验结果 {}", constraintViolations);
        errorResult(constraintViolations);

    }

    private void errorResult(Set<ConstraintViolation<Object>> constraintViolations) {
        StringBuilder errMessage = new StringBuilder();
        if (constraintViolations.size() > 0) {
            throw new ValidationException(constraintViolations.stream()
                    .map(error -> {

                        StringBuilder propertyPath = new StringBuilder().append(error.getRootBeanClass().getSimpleName()).append(".").append(error.getPropertyPath());
                        return errMessage.append(error.getMessage()).append(error.getMessage()).append("(").append(propertyPath).append(")");
                    })
                    .collect(Collectors.joining(",")));
        }
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值