自定义 Validator
前言
日常开发中过程中,时刻存在着数据的校验,即使在前端对数据进行校验的情况下,我们还是要对传入后端的数据再进行一遍校验,避免用户绕过浏览器直接通过一些 HTTP 工具直接向后端请求一些违法数据。
最普通的做法就像下面这样。我们通过 if/else 语句对请求的每一个参数一一校验。
@ApiOperation(value = "validator测试接口")
@PostMapping("/demo/validator")
public Result validatorTest(@RequestBody ValidatorDemoDTO demo) {
if (Objects.isNull(demo.getName())){
//抛出异常
throw new BaseException("name is empty");
}
return Result.success();
}
这样的代码,小伙伴们在日常开发中一定不少见,很多开源项目都是这样对请求入参做校验的。
但是,不太建议这样来写,这样的代码明显违背了 单一职责原则。大量的非业务代码混杂在业务代码中,非常难以维护,还会导致业务层代码冗杂!
这里就要介绍到 spring-boot-starter-validation
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
我们只需要验证的参数上加上了**@Valid**注解,如果验证失败,它将抛出MethodArgumentNotValidException。默认情况下,Spring 会将此异常转换为 HTTP Status 400(错误请求)。
一定一定不要忘记在类上加上 @Validated 注解了,这个参数可以告诉 Spring 去校验方法参数
自定义 Validator
在实际开发需求中,如果自带的校验注解无法满足你的需求的话,你还可以自定义实现注解。
1.第一步,你需要创建一个注解 例如:Region
代码如下(示例):
@Documented
@Constraint(
validatedBy = RegionValidator.class
)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Region {
String message() default "值不在可选范围内";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
可参考已有注解 例如:@NotBlank
@Documented
@Constraint(
validatedBy = {}
)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(NotBlank.List.class)
public @interface NotBlank {
String message() default "{javax.validation.constraints.NotBlank.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface List {
NotBlank[] value();
}
}
2.第二步,你需要实现 RegionValidator接口,并重写isValid 方法。
代码如下(示例):
public class RegionValidator implements ConstraintValidator<Region, String> {
@Override
public boolean isValid(String region, ConstraintValidatorContext constraintValidatorContext) {
//校验逻辑
String regExp = "^((1[0-5])|[1-9])?\\d$";
return region.matches(regExp);
}
}
现在你就可以使用这个注解:
@Data
public class ValidatorDemoDTO {
@NotBlank(message = "姓名不能为空")
private String name;
@NotBlank(message = "年龄不能为空")
@Region(message = "年龄值不在合理范围内")
private String age;
}
通过测试验证
@PostMapping("/demo/validator")
public Result validatorTest(@RequestBody @Valid ValidatorDemoDTO demo) {
return Result.success();
}
测试结果:
总结
JSR303 定义了 Bean Validation(校验)的标准 validation-api,并没有提供实现。Hibernate Validation是对这个规范/规范的实现 hibernate-validator,并且增加了 @Email、@Length、@Range 等注解。Spring Validation 底层依赖的就是Hibernate Validation。
JSR 提供的校验注解:
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Hibernate Validator 提供的校验注解:
@NotBlank(message =) 验证字符串非 null,且长度必须大于 0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内