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