SpringBoot整合Validation进行参数校验


 

依赖

创建时勾选 I/O -> Validation,也可以手动添加依赖

<!-- 这个依赖主要包含了 hibernate-validator -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

 
实质是使用hibernate的遗产 hibernate-validator,也可以直接引入下面的依赖

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>7.0.2.Final</version>
</dependency>

 

校验接口入参

一个字段上可以标注多个校验注解
 

直接以原生类型(jdk自带的类型,包括数组、集合)作为入参
@RestController
@Validated  //类上加 @Validated
public class TestController {

    @RequestMapping("test")  //直接在参数前加校验注解
    public String test(@NotBlank(message = "email不能为空") @Email(message = "email格式错误") String email,
                       @NotBlank(message = "密码不能为空") String password, Integer gender,
                       @Past(message = "生日只能是过去的日期") Date birthday) {
        return "ok";
    }

}

不满足校验时抛出 ValidationException,如果不捕获处理,接口直接报500;可以在全局异常处理器中统一捕获处理,e.getMessage() 拿到的message即校验校验注解的message属性值。

 

以实体类作为入参——自动校验
@RestController
//类上不用加什么校验注解
public class TestController {

    @RequestMapping("test")  //在要校验的实体类参数前面加 @Valid,此处也可以使用 @Validated
    public String test(@Valid UserVO userVO) {
        return "ok";
    }

}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserVO implements Serializable {

    private static final long serialVersionUID = 2199611382183141716L;

    @NotBlank(message = "email不能为空")
    @Email(message = "email格式错误")
    private String email;

    @NotBlank(message = "密码不能为空")
    private String password;

    private Integer gender;

    @Past(message = "生日只能是过去的日期")
    private Date birthday;

}
  • @Valid 是 spring-context 的注解,@Validated 是 hibernate-validator 包含的依赖 jakarta.validation-api 的注解,只是部分功能相同、在部分应用场景中可以互换,并不是等价的。
  • 不满足校验时,不会抛出什么异常,只是打印一个warn日志,接口返回400,也不会返回具体的校验错误提示信息。

 

手动校验参数

往往都是使用实体类接收参数,自动校验

  • 不会返回具体的校验错误提示信息,错误处理方式可能不满足我们的需要。
  • 前端发送的请求参数可能要在网关处被包装一下,前端传递的整个请求参数作为一个成员变量,再设置一些用户信息(比如解析token设置对应的userId)、灰度标识等额外数据;接收请求参数的实体不在最外层,不好直接标注 @Valid 自动校验。

所以很多时候都需要进行手动校验。
 

参数校验工具类

import org.apache.commons.collections.CollectionUtils;
import org.hibernate.validator.HibernateValidator;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 参数校验工具类
 */
public class ValidationUtils {

    /**
     * 默认校验器,使用默认校验配置
     * 校验器有多种实现,hibernate校验器只是其中一种实现
     */
    private static final Validator defaultValidator = Validation.buildDefaultValidatorFactory().getValidator();

    /**
     * 以上代码也可以这样写
     */
    private static final Validator defaultValidator1 = Validation.byDefaultProvider()
            .configure()
            .buildValidatorFactory().getValidator();

    /**
     * hibernate默认校验器
     */
    private static final Validator hibernateDefaultValidator = Validation.byProvider(HibernateValidator.class)
            .configure()
            .buildValidatorFactory().getValidator();

    /**
     * hibernate快速失败校验器
     */
    private static final Validator hibernateFailFastValidator = Validation.byProvider(HibernateValidator.class)
            .configure().failFast(true)
            .buildValidatorFactory().getValidator();

    /**
     * 隐藏构造器
     */
    private ValidationUtils() {
    }

    /**
     * 快速校验参数
     *
     * @param t   需要校验的参数
     * @param <T> 泛型,即要校验的参数类型
     * @return 错误提示信息。通过校验返回null,不通过校验则返回第一个错误字段的提示信息
     */
    public static <T> String validateFastFail(T t) {
        if (t == null) {
            return "the input parameter is null";
        }

        //校验参数,返回错误信息集合,通过校验时返回空集合(不为null)
        //因为这个校验器启用了快速失败模式,遇到第一个错误字段就返回,所以这个集合中最多只有一个元素
        Set<ConstraintViolation<T>> constraintViolations = hibernateFailFastValidator.validate(t);
        if (CollectionUtils.isEmpty(constraintViolations)) {
            return null;
        }

        return constraintViolations.iterator().next().getMessage();
    }

    /**
     * 校验参数
     *
     * @param t   要校验的参数
     * @param <T> 泛型,即要校验的参数类型
     * @return 错误提示信息集合,通过校验返回空集合
     */
    public static <T> List<String> validate(T t) {
        if (t == null) {
            return Collections.emptyList();
        }

        Set<ConstraintViolation<T>> constraintViolations = hibernateDefaultValidator.validate(t);

        return constraintViolations.stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.toList());
    }

}

在方法体中手动调用方法进行校验,无需在实体参数前加 @Valid

 

Validation常用注解

均可指定message属性值,建议都指定message,返回的错误提示比默认值更友好
 

@NotNull、@NotEmpty、@NotBlank
//不能为null,可校验任何类型
@NotNull

//不能是null、empty,可校验CharSequence、数组、集合
@NotEmpty

//不能是null、blank,可校验CharSequence
@NotBlank

 

校验指定格式
//校验数值型的值大小,可校验 大数(BigDecimal、BigInteger)、各类整型及对应的包装类型(byte、short、int、long)
//由于浮点数(float、double)存在精度问题,暂不支持
@Max(100)  //<=,最大值不能超过100,此处省略属性名value
@Min(10)
@Range(min = 10, max = 100)  //相当于@Min+@Max


//校验数值大小,可校验 大数(BigDecimal、BigInteger)、各类整型及对应的包装类型(byte、short、int、long)、CharSequence
//不要误以为名称中的Decimal是指定点;由于浮点数(float、double)存在精度问题,暂不支持
//value指定最值 String形式,inclusive可选 指定是否包含、是否可以取等,默认true
@DecimalMax(@DecimalMax(value = "100.00", inclusive = false)
@DecimalMin(value = "100.00", inclusive = false)


//校验数值位数,可校验 大数(BigDecimal、BigInteger)、各类整型及对应的包装类型(byte、short、int、long)、CharSequence
//integer指定整数部分最多4位,fraction指定小数部分最多2位
@Digits(integer = 4, fraction = 2)

//校验日期时间点,可校验jdk自带的各种日期、时间类型
@Future  //必须是未来的时间点
@Past  //必须是过去的时间点


//校验字符串长度,只能校验字符串,字符串长度(length)只能在 [5,10] 区间上
@Length(min = 5, max = 10)
//校验数组、集合、CharSequence的元素个数,元素个数只能在 [5,10] 区间上。CharSequence即字符个数
@Size(min = 5, max = 10)


//校验字符串内容,必须是指定格式
@Email  //hibernate-validator自身的 @Email 已经过时,这是 hibernate-validator 引入的 jakarta.validation-api 中的注解
@URL  //默认允许任何协议、任何host、任何端口号,注意内容不能省略http、https、ftp之类的协议,必须是完整的url
@URL(protocol = "http", host = "www.baidu.com")

以上注解只校验内容,在值不为空时才会校验内容,也就是说空值会通过校验。如果要校验非空,还需要加上@NotNull@NotBlank@NotEmpty之类的注解。

 

自定义校验规则

可以用正则表达式进行校验,示例

@Pattern(regexp = "^[1][3-9][0-9]{9}$", message = "手机号为空或格式错误")

 

多级嵌套校验

@Controller
@Validated
public class TestController {

    @RequestMapping("/handler")
    @ResponseBody
    //要校验的参数是实体类
    public User handler( @Validated User user) {
        //.....
        return user;
    }

}
@Data
public class User implements Serializable {

    //......

    @Valid  //加@Valid
    private GoodsCar goodsCar;

    @Valid  //加@Valid
    private List<Order> orderList;

}

spring的 @Validated 不支持多级嵌套校验,Validation的 @Valid 则支持。

如果要检验的参数是实体类,而这个实体类中又嵌套了实体类,如果要校验内嵌的实体类中的成员变量,还需要在内嵌的实体类成员上加@Valid。@Valid修饰的成员为null时,不会使用该实体中的校验注解进行校验;只有修饰的成员不为null时,才会使用该实体中的校验注解进行校验。

 

分组校验

多个方法都要使用某个实体类作为参数,但对这个实体类的成员变量的校验规则不同,这时可以使用分组校验。

/**
 * 定义一个类|接口作为一个分组,不一定要继承validation的Default
 * 也可以直接使用已存在的类|接口作为分组
 */
public interface OperateById {

}


public interface FindByUser extends Default {

}
@Getter
@Setter
@ToString
public class User implements Serializable {

	//使用groups指定该字段所属的分组,属于多个组时写成数组形式
    @NotNull(groups = {OperateById.class, FindByUser.class})
    private Integer id;

    @NotBlank(groups = FindByUser.class)
    private String name;

	//如果缺省groups属性,默认为Default.class,属于默认分组
	@Pattern(regexp = "[0-9]{11}")
    private String tel;

}
@Controller
@Validated
public class TestController {

    @RequestMapping("/handler")
    @ResponseBody
    //校验时指定所使用的校验规则(分组),可简写为 @Validated(FindByUser.class)。缺省value属性时默认为Default.class,使用默认分组
    public User handler( @Validated(value = FindByUser.class) User user) {
        //.....
        return user;
    }

}

校验时会使用指定分组中的校验规则进行校验。示例中

  • OperateById分组只校验id字段是否满足@NotNull
  • 默认分组只校验tel字段是否为11位的阿拉伯数字
  • FindByUser分组会校验id字段是否满足@NotNull、name字段是否满足@NotBlank,因为FindByUser继承了Default,所以也会校验默认分组中的字段

自定义的分组要不要继承Default,视需求而定。

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值