关于@Valid或@Validated注解的重复校验问题

最近在项目中使用spring内置的校验框架时,出现了一个问题:当某字段校验不通过时,对应的message信息被返回了两次,相关代码如下:

控制层

@RequestMapping(value = "/list", method = RequestMethod.POST)
@ResponseBody
public JsonResponse<ResVO> getList(@RequestBody @Valid ReqVO reqVO) {
    ResVO preOrderList = orgService.getList(reqVO);
    return JsonResponse.success(preOrderList);
}

请求入参

@Data
public class ReqVO {
    @NotNull(message = "机构编号不能为空")
    private Long orgNo;

    @NotNull(message = "操作员编号不能为空")
    private Long operatorNo;
}

当我使用postman发送如下参数的请求时,

{
	"orgNo": null,
	"operatorNo": 123
}

返回的结果为:

{
	"code": "1",
	"msg": "机构编号不能为空;机构编号不能为空",
	"data": null
}

此处的msg是经过全局异常捕获机制将校验失败抛出的异常解析后拼接的内容,理论上来说这里应该只返回一遍“机构编号不能为空”才对,很明显是有问题的。

经过各种debug,最终将关键代码定位到org.hibernate.validator.internal.engine.ValidatorImpl#validateConstraintsForDefaultGroup方法中:

private <U> void validateConstraintsForDefaultGroup(ValidationContext<?> validationContext, ValueContext<U, Object> valueContext) {
	// 获取bean(这里指 ReqVO 对象)的元数据,包含需要校验的入参对象的域、方法、构造器等
	final BeanMetaData<U> beanMetaData = valueContext.getCurrentBeanMetaData();
	final Map<Class<?>, Class<?>> validatedInterfaces = new HashMap<>();

	// 遍历 ReqVO 及其父类
	for ( Class<? super U> clazz : beanMetaData.getClassHierarchy() ) {
		BeanMetaData<? super U> hostingBeanMetaData = beanMetaDataManager.getBeanMetaData( clazz );
		boolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.defaultGroupSequenceIsRedefined();

		// if the current class redefined the default group sequence, this sequence has to be applied to all the class hierarchy.
		if ( defaultGroupSequenceIsRedefined ) {
			Iterator<Sequence> defaultGroupSequence = hostingBeanMetaData.getDefaultValidationSequence( valueContext.getCurrentBean() );
			Set<MetaConstraint<?>> metaConstraints = hostingBeanMetaData.getMetaConstraints();

			while ( defaultGroupSequence.hasNext() ) {
				for ( GroupWithInheritance groupOfGroups : defaultGroupSequence.next() ) {
					boolean validationSuccessful = true;

					for ( Group defaultSequenceMember : groupOfGroups ) {
						validationSuccessful = validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz,
								metaConstraints, defaultSequenceMember ) && validationSuccessful;
					}

					validationContext.markCurrentBeanAsProcessed( valueContext );

					if ( !validationSuccessful ) {
						break;
					}
				}
			}
		}
		// 一般走这个分支
		else {
			// 获取直接约束,“直接”指当前类自身(不包含继承),即当前类中携带了@NotNull、@NotEmpty等注解的域、方法的信息
			Set<MetaConstraint<?>> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints();
			validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz, metaConstraints,
					Group.DEFAULT_GROUP );
			validationContext.markCurrentBeanAsProcessed( valueContext );
		}

		// all constraints in the hierarchy has been validated, stop validation.
		if ( defaultGroupSequenceIsRedefined ) {
			break;
		}
	}
}

debug观察获取到的metaConstraints变量的值:

在这里插入图片描述

预期这个list应该只有两个元素,即orgNo与operatorNo字段的NotNull校验,但现在多出了getOrgNo()与getOperatorNo()方法的NotNull校验。

故怀疑是get方法出了问题,ReqVO类使用的是lombok的@Data注解自动生成的get方法,去掉@Data注解,手动生成getter和setter,问题解决。原因可能是lombok在给各字段生成getter和setter方法时,携带了字段上方的注解信息,导致spring的校验框架出现重复校验的问题。

总结:使用@Valid或@Validated配合@NotNull、@NotEmpty等注解对参数进行校验时,不能使用lombok自动生成的getter和setter,否则会导致同一个参数被校验两次(字段本身一次与get方法一次),当校验不通过时会多出一次error结果。

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值