一、@Valid使用背景

在编写接口的时候,通常会进行参数的校验,一般方法就是if语句的疯狂叠加,逻辑上是可以的,没什么问题,但是代码这样就很乱,感觉一团一样,而且不同的地方有些相似的校验还得重写,如下。

    @PostMapping("/user")
    public String addUserInfo(@RequestBody User user) {
        if (user.getName() == null || "".equals(user.getName()) {
            ......
        } else if(user.getSex() == null || "".equals(user.getSex())) {
            ......
        } 
    }

二、@Valid 作用

  • @Valid 的主要作用是用于数据效验,主要用法是:
    1. 在实体中的属性上,添加不同的注解(如下)来完成不同的校验。
    2. 在接口类中的接收数据参数中添加 @Valid 注解。
  • 实体注解详解:
    1. @NotEmpty:元素值不为null且不为空(字符串长度不为0、集合大小不为0)
    2. @NotBlank:验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格。
    3. @Null :只能为null
    4. @NotNull:不为null
    5. @Email:验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
    6. @AssertFalse:必须为false
    7. @AssertTrue:必须为true
    8. @DecimalMax(value):不大于指定值的数字
    9. DecimalMin(value):不小于指定值的数字
    10. @Digits(integer,fraction):一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
    11. @Max(value):不大于指定值的数字
    12. @Min(value):不小于指定值的数字
    13. @Future:必须是一个将来的日期(不包含现在)
    14. @FutureOrPresent:必须是一个将来的日期(包含现在)
    15. @Past :必须是一个过去的日期(不包含现在)
    16. @PastOrPresent:必须是一个过去的日期(包含现在)
    17. @Pattern(value):符合指定的正则表达式
    18. @Size(max,min):限定字符长度在min到max之间
    19. @Positive:数值是否是正数
    20. @PositiveOrZero:数值是否是正数和0
    21. @Negative:数值是否是负数
    22. @NegativeOrZero:数值是否是负数和0

注意

  • 实体类验证只是针对该实体,如果 该实体 存在 成员变量 为 实体类 ,则需要在该成员变量(假设你需要验证该实体内的变量)上加 @Valid 注解
  • @Valid 对 Get 请求中接收的平面参数请求无效。

二、@Valid 用法(导错包不生效)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

此包下的

import javax.validation.constraints

2.1、实体属性添加

1.单个实体

public class User {
    @NotBlank(message = "用户名不为空")
    private String username;
    @NotBlank(message = "密码不为空")
    private String password;
}

2.嵌套实体

public class User {
    @NotBlank(message = "用户名不为空")
    private String username;
    @NotBlank(message = "密码不为空")
    private String password;
    
    
    @Valid //不加此注解,则不会校验
    @NotNull(message = "用户住址不能为空")
    private Address address;
}
public class Address {
    @NotBlank(message = "地址详情不能为空")
    private String info;
}

2.2、接口添加

    @PostMapping("/user")
    public void addUserInfo(@RequestBody @Valid User user) {
       
    }

2.3、全局异常捕获

@RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)//最高优先级,防止有其他的全局异常捕获导致这个无法捕获
public class ExceptionAdvice {
	@ExceptionHandler(value = {MethodArgumentNotValidException.class})
	public R handleValidException(MethodArgumentNotValidException m){
			Map<String,String> errorMsg = m.getBindingResult.getFieldErrors().stream().collect(Collectors.toMap(FieldError::getField,FieldError::getDefaultMessage,(k1,k2) -> k1));
			return R.FAILED("数据异常").setData(errorMsg);
	}
}

三、分组

分组是在实体类验证中常用的一种技术,它允许你根据不同的场景对验证规则进行分组,从而在不同的情况下应用不同的验证规则。

@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class UserCopyVo {
    @NotEmpty(groups = update.class, message = 'id不能为空')
    private String id;
    @NotEmpty()
    private String userName;
    private String avatar;//头像

    public UserCopyVo(UserEntity user) {
        this.userName = user.getUserName();
        this.id = user.getUuid();
        this.avatar = user.getAvatar();
    }

    /**
     * 设置分组
     */
    public interface add extends Default {
    }

    public interface update extends Default {
    }
}


  @ApiOperation(value = 'test', notes = 'test action')
    @PostMapping('/test')
    public String test(@Validated(UserCopyVo.update.class) @RequestBody UserCopyVo u) {
        System.out.println(u);
        return 'success0000';
    }

四、@Valid拓展

有的时候,我们发现@Valid的一些注解无法满足我们的特殊开发需求,那就需要对其注解进行拓展了。
要拓展@Valid注解,可以按照以下步骤进行操作:

4.1、创建一个自定义的注解

@Constraint注解来定义这个自定义注解,并指定一个自定义的校验器类。

@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = AnnotateValidator.class)
public @interface Annotate{
    String message() default "Invalid value";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

4.2、创建一个自定义的校验器类

创建自定义校验器类实现ConstraintValidator接口,并重写initializeisValid方法。

public class AnnotateValidator implements ConstraintValidator<Annotate, String> {
    @Override
    public void initialize(Annotate annotation) {
        // 初始化校验器,可以空着
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // 执行自己的校验逻辑,返回布尔值即可
        return value != null && value.startsWith("custom");
    }
}

CustomValidator校验器类实现了ConstraintValidator<CustomValid, String>接口,其中CustomValid是自定义注解的类型,String是要校验的值的类型。

4.3、测试

在需要校验的字段或方法参数上使用自定义的注解。

public class Test{
    @CustomValid(message = "参数格式不对")
    private String customField;
}
public class MyController {
    @PostMapping("/myEndpoint")
    public void myEndpoint(@Valid @RequestBody Test myTest) {
        // ...
    }
}
Logo

鸿蒙生态一站式服务平台。

更多推荐