当前位置: 代码网 > it编程>编程语言>Java > Spring controller校验入参的方法详解

Spring controller校验入参的方法详解

2024年06月17日 Java 我要评论
问题描述项目中使用springboot,在controller中配置了@notnull和@valid,@notnull不生效,@valid生效,返回http status为400。@restcontr

问题描述

项目中使用springboot,在controller中配置了@notnull和@valid,@notnull不生效,@valid生效,返回http status为400。

@restcontroller
@requestmapping("/demo")
public class democontroller {
    @override
    @postmapping("/user")
    public createuserrsp createuser(
        @notnull @size(min = 1, max = 64) @requestheader(value = "token") string token,
        @notnull @valid @requestbody createuserreq createuserreq) {
        // 业务逻辑
    }
}

原因分析

controller接收到请求,首先会进行参数解析,解析相关的类:

为什么@requestbody中的@valid生效了?

参数中@requestbody注解是使用requestresponsebodymethodprocessor解析的,下面重点看下这个。

public object resolveargument(methodparameter parameter, @nullable modelandviewcontainer mavcontainer, nativewebrequest webrequest, @nullable webdatabinderfactory binderfactory) throws exception {
	parameter = parameter.nestedifoptional();
	object arg = this.readwithmessageconverters(webrequest, parameter, parameter.getnestedgenericparametertype());
	string name = conventions.getvariablenameforparameter(parameter);
	if (binderfactory != null) {
		webdatabinder binder = binderfactory.createbinder(webrequest, arg, name);
		if (arg != null) {
                // 重点
			this.validateifapplicable(binder, parameter);
			if (binder.getbindingresult().haserrors() && this.isbindexceptionrequired(binder, parameter)) {
				throw new methodargumentnotvalidexception(parameter, binder.getbindingresult());
			}
		}

		if (mavcontainer != null) {
			mavcontainer.addattribute(bindingresult.model_key_prefix + name, binder.getbindingresult());
		}
	}

	return this.adaptargumentifnecessary(arg, parameter);
}
protected void validateifapplicable(webdatabinder binder, methodparameter parameter) {
	annotation[] annotations = parameter.getparameterannotations();
	annotation[] var4 = annotations;
	int var5 = annotations.length;

	for(int var6 = 0; var6 < var5; ++var6) {
		annotation ann = var4[var6];
        // 重点,解析参数的注解
		object[] validationhints = validationannotationutils.determinevalidationhints(ann);
		if (validationhints != null) {
            // 执行校验
			binder.validate(validationhints);
			break;
		}
	}

}

可以看出,@valid和@validated注解都可以解析到:

public static object[] determinevalidationhints(annotation ann) {
	if (ann instanceof validated) {
		return ((validated)ann).value();
	} else {
		class<? extends annotation> annotationtype = ann.annotationtype();
		if ("javax.validation.valid".equals(annotationtype.getname())) {
			return empty_object_array;
		} else {
			validated validatedann = (validated)annotationutils.getannotation(ann, validated.class);
			if (validatedann != null) {
				return validatedann.value();
			} else {
				return annotationtype.getsimplename().startswith("valid") ? convertvalidationhints(annotationutils.getvalue(ann)) : null;
			}
		}
	}
}

为什么@requestheader中的@notnull没有生效?

按照上面的思路,我们看下requestheadermapmethodargumentresolver,里面并没有调用validate相关的代码。

怎么样才能生效?

在类上加@validated。并且加maven依赖

<dependency>
	<groupid>org.springframework.boot</groupid>
	<artifactid>spring-boot-starter-validation</artifactid>
</dependency>

@validated生效原理

后处理器methodvalidationpostprocessor中给使用了@validated注解的类创建了个切面。实际执行切面逻辑的是methodvalidationinterceptor

public class methodvalidationpostprocessor extends abstractbeanfactoryawareadvisingpostprocessor implements initializingbean {
    private class<? extends annotation> validatedannotationtype = validated.class;
	public void afterpropertiesset() {
		pointcut pointcut = new annotationmatchingpointcut(this.validatedannotationtype, true);
		this.advisor = new defaultpointcutadvisor(pointcut, this.createmethodvalidationadvice(this.validator));
	}

	protected advice createmethodvalidationadvice(@nullable validator validator) {
		return validator != null ? new methodvalidationinterceptor(validator) : new methodvalidationinterceptor();
	}
}

请求执行时,methodvalidationinterceptor中先判断方法和类上有没有@validated,

public object invoke(methodinvocation invocation) throws throwable {
	if (this.isfactorybeanmetadatamethod(invocation.getmethod())) {
		return invocation.proceed();
	} else {
        // 方法和类上有没有@validated
		class<?>[] groups = this.determinevalidationgroups(invocation);
		executablevalidator execval = this.validator.forexecutables();
		method methodtovalidate = invocation.getmethod();
		object target = invocation.getthis();
		assert.state(target != null, "target must not be null");

		set result;
		try {
            // 校验
			result = execval.validateparameters(target, methodtovalidate, invocation.getarguments(), groups);
		} catch (illegalargumentexception var8) {
			methodtovalidate = bridgemethodresolver.findbridgedmethod(classutils.getmostspecificmethod(invocation.getmethod(), target.getclass()));
			result = execval.validateparameters(target, methodtovalidate, invocation.getarguments(), groups);
		}

		if (!result.isempty()) {
            // 校验失败的异常
			throw new constraintviolationexception(result);
		} else {
			object returnvalue = invocation.proceed();
			result = execval.validatereturnvalue(target, methodtovalidate, returnvalue, groups);
			if (!result.isempty()) {
				throw new constraintviolationexception(result);
			} else {
				return returnvalue;
			}
		}
	}
}

实际校验的类是validatorimpl。代码一直跟下去,能找到最终执行校验的地方。---注意,validatorimpl已经是hibernate-validator提供的了。

private void validatemetaconstraints(basebeanvalidationcontext<?> validationcontext, valuecontext<?, object> valuecontext, object parent, iterable<metaconstraint<?>> constraints) {
	iterator var5 = constraints.iterator();

	while(var5.hasnext()) {
		metaconstraint<?> metaconstraint = (metaconstraint)var5.next();
		this.validatemetaconstraint(validationcontext, valuecontext, parent, metaconstraint);
		if (this.shouldfailfast(validationcontext)) {
			break;
		}
	}

}

总结

controller中requestbody中直接可以用@valid或@validated校验,如果想校验方法中单个参数,需要在方法或类上加@validated,这样会开启方法校验的切面,切面中会拿到方法签名中每个字段的注解然后进行校验。

以上就是spring controller校验入参的方法详解的详细内容,更多关于spring controller校验入参的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com