SpringBoot Validation
Validation
使用springboot的validation进行参数校验。
引入依赖
1 | |
注解
在validation中定义了一系列约束(constraint)注解。
| 注解 | 功能 |
|---|---|
| @NotNull | 不能为Null,可以为空 |
| @Null | 必须为null |
| @NotBlank | 字符串不能为null,trim()后不能为"",至少有一个非空白字符 |
| @NotEmpty | 不能为null,集合,数组,mao等size不能为0,字符串可以是空白字符 |
| @Max(value) | 注解元素须是数值类型,不支持float,double,最大不超过该值 |
| @Min(value) | 注解元素须是数值类型,不支持float,double,最小不小于该值 |
| @Positive | 判断数字是否为正数 |
| @PositiveOrZero | 判断正数或零 |
| @Negative | 判断数字是否为负数 |
| @NegativeOrZero | 判断负数或零 |
| @Digits(integer,fraction) | 必须为数字,且整数位数和小数位数必须在指定范围 |
| @Size(min,max) | 字符串长度,或集合,数组,map等size必须在指定范围内, |
| 字符串必须是email格式 | |
| @Length(min,max) | 注解对象只能是字符串,字符串长度必须在指定范围 |
| @AssertFalse | 可以为null,不为null必须为false |
| @AssertTrue | 可以为null,不为null必须为true |
| @Past | 注解元素须为日期时间,判断是否是过去的日期 |
| @PastOrPresent | 判断日期是否是过去或现在 |
| @Future | 注解元素须为日期时间,判断是否是未来的日期 |
| @FutureOrPresent | 判断日期是否是未来或现在 |
| @Pattern(value) | 判断是否符合正则表达式 |
@Valid和@Validated
@Valid可以添加在方法参数,方法返回,成员变量,普通方法,构造方法上,表示需要进行约束校验。
@Validated可以添加在类,方法参数,普通方法上,进行约束校验,支持分组校验。
一般来说,这两个都放在要加校验的方法参数前。
两者容易搞混。
它们的区别有:
@Valid可以递归校验,即如果A类中有一个B类变量,而B类中也有约束注解,那么在检查A类约束时,会自动检查B类中的成员变量,而@Validated不会检查@Validated可以分组校验,而@Valid不行
一般来说,在控制器方法参数前使用@Validated,在实体类的复杂对象前,使用@Valid。
例如,实体类User中有一个Address变量:
1 | |
1 | |
在控制器方法中可以这样写:
1 | |
就会递归校验User和Address的成员变量约束注解。
异常处理
校验参数后,会将校验结果发到一个BindingResult类中,如果出现错误,会抛出异常,所以需要手动处理异常。
可以这样处理,在控制器方法中处理:
1 | |
如果使用这种处理方法,需要在每个控制器方法中都这样写,冗余很高,因此一般不使用这种方法。
全局异常处理
往往是创建一个全局异常类来集中处理所有异常。
使用@ControllerAdvice和@RestControllerAdvicce来注解全局异常处理类。
使用@ExceptionHandler(class)放在方法上,来指定要处理的异常类。
1 | |
在使用@Validated和@Valid注解校验参数出错后,会抛出MethodArgumentNotValidException异常,它是BindException的子类。
可以直接处理MethodArgumentNotValidException,也可以处理BindException。一般是将错误信息以统一的json的格式返回给前端。
## 分组校验
很多时候,在校验参数时,有不同的情况。例如在新增用户时,用户名不能为空,但是在修改用户信息时,用户名可以为空。
因此,分组校验就是针对相同的参数或对象,在不同情况下使用不同的校验规则。
首先定义两个分组接口Create和Update,继承Default接口。
继承Default不是必须的,如果继承了Default,那么@Validated(value=Create.class)的校验范畴就是Create和Default;如果没有继承Default,那么@Validated(value=Create.class)的校验范畴只有Create,只有手动指定Default:@Validated(value={Create.class,Default.class}),校验范畴才是Create和Default
1 | |
1 | |
在实体类中,给参数加校验注解时,指定所属分组。
1 | |
当没有指定分组时,默认是Default分组,因此成员变量address的groups可以不写。
然后在控制器方法中启动校验时,使用@Validated的value属性指定校验分组。
1 | |
在上述这种情况下,隶属于Create和Default组的校验设置,都会触发校验。
其他情况同理。
自定义约束
开发自定义约束需要两步:
- 编写自定义约束的注解
- 编写自定义约束的校验器ConstraintValidator
以开发一个枚举校验为例。
首先创建自定义约束注解:
1 | |
其中@Constraint(validatedBy = EnumConstraintValidator.class)指定了使用EnumConstraintValidator类校验该注解。
value是自定义的成员变量,其他三个是所有约束注解都要有的成员变量。
message是检验出错的默认输出信息。
groups是分组信息,payload不用管。
然后创建EnumConstraintValidator类:
1 | |
继承了ConstraintValidator<A,T>接口,A表示自定义约束注解,T表示参数值的类型。有两个方法。
在初始化时,可传入注解中的value变量数组,然后在isValid方法中,将实际输入与value数组中的值一一比较,如果匹配成功,返回true,否则返回false。