分享免费的编程资源和教程

网站首页 > 技术教程 正文

springboot中注解校验@Valid@Validated失效场景汇总(持续更新)

goqiw 2024-09-24 16:05:01 技术教程 31 ℃ 0 评论

1.单个参数校验失效


1.1问题说明


springboot中注解校验@Valid@Validated使用详细说明 中提到过接口中对单个参数进行校验问题.

?编辑现在发现问题(使用另一个接口说明此问题):


如果传递0,自定义异常正常显示:



如果字段不传递值或是直接省略字段:



?编辑 参数为null,能进入到接口中,说明添加的@Min注解对为null的情况不起作用.


1.2原因分析


@Min注解对应的公共处理类AbstractMinValidator.java,校验逻辑中对传递参数为null默认处理是校验通过的,源码如下:


public abstract class AbstractMinValidator<T> implements ConstraintValidator<Min, T> {

	protected long minValue;

	@Override
	public void initialize(Min maxValue) {
		this.minValue = maxValue.value();
	}

	@Override
	public boolean isValid(T value, ConstraintValidatorContext constraintValidatorContext) {
		// null值默认通过校验
		if ( value == null ) {
			return true;
		}
        
        // 不为null进入各种类型数字的实现类进行比较大小.
		return compare( value ) >= 0;
	}

	protected abstract int compare(T number);
}



1.3处理方案(两种)


1.接口请求参数中添加@NotNull注解,hibernate框架支持多个校验注解进行校验;


    @GetMapping("/XXX")
    public ApiResult findStudioDetailVo(@NotNull(message = "瑜伽馆id不允许为空")
                                            @Min(value = 1,message = "瑜伽馆id不允许为0") Integer studioId){
        StudioDetailVo studioDetailVo = appointCourseService.findStudioDetailVo(studioId);
        return ApiResult.ok(studioDetailVo);
    }



测试三种请求都会有对应提示:




2.自定义校验注解(实际就是对AbstractMinValidator中对null值不进行处理的逻辑进行修改),自定义注解可以参考:springboot中注解校验@Valid@Validated使用详细说明中2.5 自定义注解进行实现.


对于处理方案2中可以对AbstractMinValidator中的校验逻辑进行重写,没有试过是否可行,自定义注解看起来是一个比较笨重的办法,如果有实现过AbstractMinValidator中isValid重写方案的大佬可以评论区留言!


2.分组校验失效问题


2.1问题说明


springboot中注解校验@Valid@Validated使用详细说明

中2.3分组校验:需要添加Default.class,否则请求参数中注解没有添加group属性字段会出现注解失效的情况.定位源码发现请求接口中@Validated注解属性一般是添加分组校验的类型,支持添加多个;校验框架会根据@Validated当前支持的分组类型遍历请求参数中的字段的分组类型是否一致,如果一致就会进入注解校验的实现逻辑里面,如果不一致会进行匹配下一个分组.直到所有的结束!


以案例中的请求示例进行简单说明:


public class CourseTableDto {
    @Min(value = 1,message = "瑜伽馆id不允许为空")
    private int studioId;
    @Range(min = 1,max = 2,message = "课程类型:1.团课,2小班课",groups = CourseTableDtoType.class)
    private int type;
    @Pattern(regexp = "((20)[0-9]{2})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])",message = "时间格式支持:yyyy-MM-dd")
    private String dateTime;
    
// 省略get/set
}



@PostMapping("/findPersonalTeacherListByDay")
    public ApiResult findStudioCourseTablesByDay(@RequestBody @Validated(value = {CourseTableDtoType.class,Default.class}) CourseTableDto courseTableDto){
      // 业务实现已忽略
    }



请求接口中第一个分组属性是CourseTableDtoType.class,框架会遍历检查三个请求参数中是否有此分组,其中只有type有相同分组属性,所以第一次分组只会校验type;第二个分组属性是Default.class,框架会遍历检查三个请求参数中是否有此分组属性,此时studioId与dateTime有此属性,所以会对两个属性进行注解校验处理.这也就是对于支持分组校验接口中,除了要添加自定义分组属性之外需要添加默认分组属性的原因,实际上就是保证支持分组的情况下,默认分组属性的字段都能进行校验!


2.2源码分析


具体源码体现如下:


ValidatorImpl中validateMetaConstraint的具体实现逻辑:


	private boolean validateMetaConstraint(ValidationContext<?> validationContext, ValueContext<?, Object> valueContext, Object parent, MetaConstraint<?> metaConstraint) {
	    // 代码省略.................

        // 判断当前字段所标注的注解是否需要进行校验
		if ( isValidationRequired( validationContext, valueContext, metaConstraint ) ) {

	        // 省略部分代码

            // 字段上注解校验的实现
			success = metaConstraint.validateConstraint( validationContext, valueContext );
            
            // 省略部分代码

		}

		// reset the value context to the state before this call
		valueContext.resetValueState( originalValueState );

		return success;
	}



isValidationRequired中具体的判断逻辑


private boolean isValidationRequired(ValidationContext<?> validationContext,
			ValueContext<?, ?> valueContext,
			MetaConstraint<?> metaConstraint) {
	
        // 省略部分代码....
        // 分组校验判断的重要逻辑:metaConstraint表示当前注解上支持的分组类型,valueContext表示当前参数对象支持的分组类型,如果一致则进行校验,如果不一致则对该字段不进行注解校验
		if ( !metaConstraint.getGroupList().contains( valueContext.getCurrentGroup() ) ) {
			return false;
		}
		
        // 省略部分代码...
		);
	}



以上是对实际开发过程中遇到的@Valid@Validated失效场景汇总,如果条友有其他失效场景欢迎评论区留言,共同研究!

—— END ——

作者| 卖柴火的小男孩啊

多年后端开发经验,坚持分享更多java干货内容!

如果你喜欢我的文章,不妨点赞、转发、收藏一下哦!

?

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表