SpringBoot的请求参数校验注解
Validation 和 validator 包提供了⼀系列校验⽤注解,帮助我们在 RESTful 服务请求中实现期望的数据校验,其注解的功能包括但不限于⼊参的存在性判断、⾮空判断、数值取值范围限定、特定含义数据格式校验、校验失败提⽰信息等。
Maven 依赖
在 SpringBoot 2.3 版本之前的项⽬中,主要需要添加的依赖包括以下两个。其中 spring-boot-starter-web 包含了 spring-boot-starter-validation 这个 maven 包,⽽⾥⾯则添加了 javax-validation 和 hibernate-validator 两个包含检验注解的依赖包。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
⽽在 SpringBoot 2.3 之后的版本则需要单独引⼊ spring-boot-starter-validation 这个 maven 包。
常⽤注解说明
Maven 包:straints:validation-api-2.0.1.Final
注解作⽤数据类型说明
@Null任何类型对象必须为空
@NotNull任何类型对象不为空
@NotBlank字符串对象不为空,字符串去掉前后空格后长度不为0
@NotEmpty字符串、集合、数组对象不为空,且字符串长度不为0,集合、数组⼤⼩不为0
@AssertTrue布尔型必须为true;null值有效,Boolean通过校验,boolean不可
@AssertFalse布尔型必须为false;null 可通过校验
@Min(number)整型数数值必须⼤于或等于指定的最⼩值
@Max(number)整型数数值必须⼩于或等于指定的最⼤值
@DecimalMin(decimal)浮点型数值必须⼤于或等于指定的最⼩值,内部使⽤BigDecimal定义数值对象;为 null 是校验通过;默认
包含边界值
@DecimalMax(decimal)浮点型数值必须⼩于或等于指定的最⼤值,内部使⽤BigDecimal定义数值对象;为 null 是校验通过;默认
包含边界值
@Positive整型数数值必须为正整数
@PositiveOrZero整型数数值必须为正整数或0 @Negative整型数数值必须为负整数
@NegativeOrZero整型数数值必须为负整数或0 @Digits数值型或者字符串作为数值其构成必须合法
@Digits(integer=,
fraction=)
数值型数值必须符合指定的整数精度和⼩数精度
@Size(min=, max=)字符串、集合、数组对象的⼤⼩在指定区间内;为 null 是校验通过
@Past Date或者Calendar
对象
必须是⼀个过去的⽇期
@PastOrPresent Date或者Calendar
对象
必须是⼀个过去或者当前的⽇期
@Future Date或者Calendar
对象
必须是⼀个将来的⽇期
@FutureOrPresent Date或者Calendar
对象
必须是⼀个将来或者当前的⽇期
@Pattern字符串必须是规则正确的正则表达式
@Email字符串必须是Email类型;可以通过指定regexp和flags来⾃定义email格式;为null时算作通过验证
注解作⽤数据类型说明
关于 @AssertTrue 与 @AssertFalse 注解对 null 的校验说明,需要区分基本类型(boolean)与装箱类型(Boolean)两种情况。
参数为null,校验时则将参数看作其数据类型对应的默认值。boolean 型的默认值为 false,因此 @AssertTure 验证失败;Boolean 型则仍为 null,根据 javadoc 的说明 “null elements are considered valid”,因此都认为是校验通过。
straints 包仅仅定义了上⾯的⼀系列注解,真正的实现逻辑在 org.hibernate.validator 包中,在 SpringBoot 项⽬启动时⼀般都会⾃动引⼊ hibernate 包。
Maven 包:org.straints:hibernate-validator-6.0.18.Final
注解作⽤数据类型说明
@Length(min=, max=)字符串字符串的长度在指定区间内
@Range(min=, max=)数值型数值必须在指定闭区间内
@CreditCardNumber字符串必须是通过Luhn校验和测试的信⽤卡号码
@URL字符串必须是URL地址
@UniqueElements集合类校验集合中的值都是唯⼀的,null 视为有效成员
@Validated实现字段分组校验
在请求参数中添加了上述注解后,还需要在接⼝接收的参数前加上 @Validated 注解,这些校验的注解才会⽣效,不加的话则是⽆效的。在 controller 接⼝被调⽤时,上述注解对应的⼊参将会进⾏校验,但这种校验⼀旦接⼝被调⽤则是必须执⾏的,⽽不能根据调⽤调⽤业务的不同⽽进⾏分组处理。例如,在执⾏添加操作时,⼀般是多个参数都要求⾮空,个别参数需要满⾜其含义的约束;⽽在执⾏删除操
作时,通常只需要⼀个起标识作⽤的 id 或者 seq ⾮空,其他参数⼀般都可以为空。为了以⼀种⽐较友好的代码逻辑实现这种需求,这⾥就可以引⼊分组校验的功能。
⾸先,可以根据业务的需要定义⼀些分组接⼝:
public abstract interface InsertGroup {}
public abstract interface UpdateGroup {}
public abstract interface DeleteGroup {}
然后,在参数使⽤注解校验的地⽅加⼊响应的分组,说明只有采⽤指定分组时,才会对参数进⾏校验:
@NotBlank(message="seq不能为空", groups ={DeleteGroup.class})
private String seq;
@NotBlank(message="seq不能为空", groups ={DeleteGroup.class})
@Length(message="标题长度要求在{min}和{max}之间", min =5, max =20, groups ={InsertGroup.cla
ss, UpdateGroup.class})
private String title;
最后,在 controller 接⼝的请求参数中通过 @Validated 指定校验使⽤的分组:
@PostMapping("/add")
public Response add(@RequestBody@Validated({InsertGroup.class}) Request req){
return new Response.success();
}
@PostMapping("/update")
public Response update(@RequestBody@Validated({UpdateGroup.class}) Request req){
return new Response.success();
}
@PostMapping("/delete")
public Response delete(@RequestBody@Validated({DeleteGroup.class}) Request req){
return new Response.success();
}
@Valid
@Valid 注解⾃ Spring 中就开始使⽤,我们常常⽤它进⾏⽅法级别或者成员属性级别的校验。与 @Validated 相⽐,它常⽤于触发嵌套属性的验证,⽽不⽀持分组验证。这⾥的嵌套属性,指的是在请求体内部,将 @Valid 注解使⽤在⼀个类对象上,以触发对这个对象内部每⼀个嵌套属性的注解校验。举个例⼦:
public class ApplyForm {
// ...
@Valid
@NotNull
private PersonalInfo personalInfo;
// ...
}
⾃定义校验注解
参照 @NullBlank 注解,定义了⼀个如下的⾃定义注解:
@Documented
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(DemoLength.List.class)
@Constraint(validateBy ={DemoLengthValidator.class})
public@interface DemoLength {
long min()default1;
long max()default10;
String message()default"DemoLength validator annotation.";
Class<?>[]groups()default{};
Class<?extends Payload>[]payload()default{};
@Documented
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public@interface List {
DemoLength[]value();
}
}
这是⼀个⽤于校验参数长度的检验注解,其可指定长度最⼩值、最⼤值、以及提⽰信息
@Constraint(validateBy = {DemoLengthValidator.class}) 指定⾃定义的注解校验类
public class DemoLengthValidator implements ConstraintValidator<DemoLength, Object>{
private long min;
private long max;
@Override
public void initialize(DemoLength constraintAnnotation){
this.min = constraintAnnotation.min();
this.max = constraintAnnotation.max();
}
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext){
if(o ==null){springboot aop
return true;
}
if(o instanceof String){
int length ((String) o).length();
return min <= length && length <= max;
}else if(o instanceof Integer || o instanceof Long){
long val =((Number) o).longValue();
return min <= length && length <= max;
}
return false;
}
}
注解校验类实现了 ConstraintValidator 接⼝,接⼝需要指定注解类以及注解应⽤的参数类型,这⾥的 DemoLength 注解即可⽤于判断字符串长度,也可⽤于判断数值⼤⼩,因此参数类型可以指定为 Object。实现接⼝主要是为了实现两个⽅法,顾名思义,initialize ⽤于初始化注解的配置信息,isValid 则是具体的校验判断逻辑,其中这⾥将 null 视为有效参数。
注解的校验逻辑
之所以在 Controller 层的请求体参数使⽤校验注解即能完成校验,是因为 Spring 启动时会为 Controller 类添加⼀个MethodValidationInterceptor,每当有请求进来的时候,会对亲求进⾏拦截,然后判断请求体或者⽅法是否带有 @Valid 或者
@Validated 注解。如果存在,那么请求会被 AOP 拦截且执⾏参数校验:校验通过,则可以继续执⾏后续的业务逻辑;校验不通过,则抛出ConstraintViolationException 异常,携带配置的特定提⽰信息返回给请求⽅。具体源码可见 MethodValidationInterceptor 类。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。