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

  • Post author:
  • Post category:其他


1.单个参数校验失效

1.1问题说明


springboot中注解校验@Valid@Validated(亲测有效)_卖柴火的小伙子的博客-CSDN博客

中提到过接口中对单个参数进行校验问题.

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

如果传递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(亲测有效)_卖柴火的小伙子的博客-CSDN博客

中2.5 自定义注解进行实现.

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

2.分组校验失效问题

2.1问题说明


springboot中注解校验@Valid@Validated(亲测有效)_卖柴火的小伙子的博客-CSDN博客

中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;
		}
		
        // 省略部分代码...
		);
	}



版权声明:本文为weixin_43401380原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。