@小仙女的胖鱼(pycrab)
标签:开发规范参数校验
一、校验规则
JSR303(Bean Validation)是Java提供的一项标准规范,它含有一些基本的constraint,比如@NotNull。Hibernate Validator参考Bean Validation实现了其他的一些校验规则,比如:
- @NotBlank(message = “”) 验证字符串非null,且trim后长度必须大于0
- @URL
- @Range
- @Length
- @Pattern() 正则校验
结合这两者,我们就可以进行基本的参数校验。Spring Boot默认使用hibernate-validator进行校验,首先我们需要导入相关依赖,Spring Boot Web项目的话肯定会引入spring-boot-starter-web依赖,其中已经包含了hibernate-validator,故无需引入(否则需要引入spring-boot-starter-validation或者hibernate-validator)。
二、实体属性校验
该校验基于使用实体对象来接收请求参数,参数和实体属性对应。比如@RequestBody接受请求体参数和用实体类封装的GET请求参数。
1、基本使用
/**
* 1、通过两个步骤即可实现普通对象校验
*/
// 实体属性添加校验规则
@NotBlank(message = "标题不能为空")
private String title;
// 接口方法实体对象参数前面添加@valid注解
public R save(@RequestBody @Valid SaveDTO dto){
}
/**
* 2、如果需要进行嵌套校验,即实体类中的属性是一个实体对象,则需要在以上基础上,
* 在嵌套属性上再添加一个@Valid注解
*/
@Valid
private List<SaveDTO> dto;
/**
* 3、如果参数是数组对象,则需要在以上基础上,在类上面添加@Validated注解
*/
@Validated
public class FileController {
@PostMapping
public R saveFiles(@RequestBody @Valid List<File> files) { // Java 8可以将@Valid放在<>内
}
}
2、分组校验
如果对同一个参数在不同情况下使用不同的校验规则(比如增加和修改操作),这时候就需要使用分组的功能,使用分组校验与之前的稍有不同,分为三个步骤:
- 创建代表不同分组的空接口,未指定分组的属性属于Default分组
- 在校验规则注解上添加group属性
- 在接口实体对象参数前面添加@Validated注解,并指明分组
```java
/**
- 1、先声明三个空接口 */ public interface AddGroup{} public interface EditGroup{}
// 该接口定义分组的校验顺序 @GroupSequence({EditGroup.class, AddGroup.class, Default.class}) public interface Group { }
/**
- 2、声明所属分组 */ @NotBlank(message = “标题不能为空”, group = {AddGroup.class}) private String title;
/**
3、指定校验组别 */ public R save(@RequestBody @Validated({AddGroup.class, Default.class}) SaveDTO dto){ } ```
3、自定义校验
如果已有的校验规则不适用具体场景,我们就需要自定义规则来校验,主要有两个步骤,创建自定义注解和实现自定义规则,然后注解和普通注解一样使用即可。
- 创建自定义注解 ```java /*
- 元注解 */ @Retention(RetentionPolicy.RUNTIME) // 指定注解的生命周期,这里表示运行时仍可用 @Target({ElementType.FIELD}) // 指定注解的使用范围,这里表示用在实体属性上 @Inherited @Documented
/*
其他声明注解 */ @Constraint(validatedBy = CheckPrimeNumberValidator.class) // 指定注解的处理类 public @interface CheckPrimeNumber { String message() default “该数字不是素数”; //String value(); Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
- 实现自定义规则,创建一个自定义校验类实现ConstraintValidator类,重写其中的initialize方法和isValid方法
```java
public class CheckPrimeNumberValidator implements ConstraintValidator<CheckPrimeNumber, Integer> {
@Override
public void initialize(CheckPrimeNumber checkPrimeNumber) {
}
@Override
public boolean isValid(Integer prime, ConstraintValidatorContext constraintValidatorContext) {
// 验证逻辑
Boolean flag = true;
if (prime != null) {
if (prime < 2) {
return false;
}else {
for (int i = 2; i <= Math.sqrt(prime); ++i) {
if (prime % i == 0) {
flag = false;
break;
}
}
}
}
return flag;
}
}
有这样一个需求,根据实体类的某个字段的值决定要不要校验其它属性,这里也进行自定义校验规则实现: ```java /**
自定义类注解 */ @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = SubValidator.class) public @interface ValidateSub { String message() default “”;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {}; }
/**
自定义校验类 */ public class SubValidator implements ConstraintValidator
{ @Override public void initialize(ValidateSub validateSub) { } @Override public boolean isValid(WorkOrderMakeUpDTO.SubDTO subDTO, ConstraintValidatorContext constraintValidatorContext) {
/**
* 这里需要手工判断,可以在没有到达业务前进行判断
*/
// 根据该字段判断是否需要校验
if (subDTO.isEnabled())
{
// 禁用默认的校验规则
constraintValidatorContext.disableDefaultConstraintViolation();
if (StringUtils.isBlank(subDTO.getClassId()))
{
// 添加新的校验提示
constraintValidatorContext.buildConstraintViolationWithTemplate("订阅资源标识不能为空").addConstraintViolation();
return false;
}
if (StringUtils.isBlank(subDTO.getClassName()))
{
constraintValidatorContext.buildConstraintViolationWithTemplate("订阅资源名称不能为空").addConstraintViolation();
return false;
}
}
return true;
} } ```
三、方法参数校验
JSR303和Hibernate validator的校验只能对实体类的属性进行校验,不能对单个方法参数进行校验,spring在此基础上进行了扩展,添加了MethodValidationPostProcessor拦截器,可以实现对方法参数的校验,通过三个步骤实现:
实例化MethodValidationPostProcessor,实现如下:
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
在接口类上添加@Validated注解
- 在接口方法的参数前面添加普通校验规则即可
四、自定义校验配置
通过以上方式校验请求参数,返回的校验结果总是全部参数的校验结果,我们需要的是一次校验一个,返回一个校验异常的时候,就需要自定义校验配置类来实现,代码如下:
@Configuration
public class CustomValidatorConfig {
@Bean
public Validator validator(){
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
.failFast(true)
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
}