HibernateValidator校验最佳实践

几年前刚学习 SpringBoot 的时候,就接触并开始使用 HibernateValidator 校验框架,注解校验结合统一异常处理,对代码的整洁性提高特别有帮助。但是目前发现公司里比较新的项目中对参数进行校验还是使用以前传统的方式,需要逐一对请求对象中的属性使用 if 来判断合法性,当需要校验的属性很多时,一大坨的 if 判断校验代码就不可避免。本文介绍 HibernateValidator 校验框架的日常使用,不涉及自定义约束注解。

环境配置
首先需要引入依赖,需要注意是否依赖有冲突,如果有一定要解决掉冲突

org.hibernate.validator hibernate-validator 6.1.0.Final 依赖引入后,Spring 环境需要配置一下校验器。
@Configuration
public class ValidatorConfig {

    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                // 快速失败
                .failFast(true)
                .buildValidatorFactory();
        return validatorFactory.getValidator();
    }
}

这里设置 failFast 为 true,代表着如果有多个参数的情况下,只要校验出一个参数有误就返回错误而不再继续校验。

为对象添加校验注解
@Data
public class StudentVo {

@NotNull(message = "学号不能为空")
private Integer id;

@NotNull(message = "姓名不能为空")
private String name;

@NotNull(message = "邮箱地址不能为空")
@Email(message = "邮箱地址不正确")
private String email;

private Integer age;

}
Hibernate Validator 是对 JSR 349 验证规范的具体实现,相关的常用注解 我贴在文末以供参考。

请求接口处理

@PostMapping("/create")
public Result<StudentVo> create(@Validated StudentVo student) {
  System.out.println(student.toString());
  return Result.success(student);
}

@PostMapping("/update")
public Result<StudentVo> update(@Validated StudentVo student) {
  System.out.println(student.toString());
  return Result.success(student);
}

注意 @Validated 是 org.springframework.validation.annotation.Validated,不要引入错了。加了这个注解之后,就会自动会参数进行校验。如果校验不通过,会抛出 BindException 或者 MethodArgumentNotValidException这两个异常中的一个异常,一般项目中为了规范接口返回值,都会进行统一异常处理。

校验异常统一异常处理

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BindException.class)
    @ResponseBody
    public Result validateErrorHandler(BindException e) {
        ObjectError error = e.getAllErrors().get(0);
        return Result.fail(error.getDefaultMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public Result<?> validateErrorHandler(MethodArgumentNotValidException e) {
        ObjectError error = e.getBindingResult().getAllErrors().get(0);
        return Result.fail(error.getDefaultMessage());
    }

    @ExceptionHandler(Throwable.class)
    @ResponseBody
    public Result<?> handleException(HttpServletRequest request, Throwable ex) {
        return Result.fail(ex.getMessage());
    }
}

因为配置校验为 failFast,因此错误信息中只会有一条记录。

第一次测试
curl -i -X POST -d “name=张三” ‘http://localhost:8082/learning-validator/create’
只填写了 name 参数,而 id 与 email 未填写的情况下进行请求,返回结果

{“success”:false,“msg”:“学号不能为空”}
按照一般开发逻辑而言,create 接口是不需要传递 id 参数的,但是 update 接口一般必须要传 id 参数,难不成用这个这个校验框架后需要写两个对象么?其实不是的。这就引出了 校验分组 的概念,即可以选择校验某些组的属性进行校验。

校验分组
首先需要创建两个分组,CreateGroup 与 UpdateGroup,分别代表着新增时需要校验与更新时需要校验。两个组都是空的接口,只是作为一个标记类使用。

public interface CreateGroup {
}
public interface UpdateGroup {
}

接着修改请求对象,分别指定哪些对象是哪些组需要校验的,如果不指定组,默认都会进行校验。

@Data
public class StudentVo {
    @NotNull(groups = UpdateGroup.class, message = "学号不能为空")
    private Integer id;

    @NotNull(message = "姓名不能为空")
    private String name;

    @NotNull(groups = CreateGroup.class, message = "邮箱地址不能为空")
    @Email(groups = CreateGroup.class, message = "邮箱地址不正确")
    private String email;
  
    private Integer age;
}

本文示例中请求对象,id 属性只在更新时校验;name 属性无论是新增还是更新都要校验;email 属性只在新增时进行校验,而 age 属性因为未指定校验注解,因此不进行校验,这里的 groups 可以是多个分组。

指定属性的分组之后,控制器接口也需要指定使用哪一个组来进行校验。

@PostMapping("/create")
public Result<StudentVo> create(@Validated(CreateGroup.class) StudentVo student) {
  System.out.println(student.toString());
  return Result.success(student);
}

@PostMapping("/update")
public Result<StudentVo> update(@Validated(UpdateGroup.class) StudentVo student) {
  System.out.println(student.toString());
  return Result.success(student);
}

第二次测试
curl -i -X POST -d “name=张三” ‘http://localhost:8082/learning-validator/create’
只填写了 name 参数,而 id 与 email 未填写的情况下进行请求,返回结果:

{“success”:false,“msg”:“邮箱地址不能为空”}
填写 name 参数以及 email 参数进行请求

curl -i -X POST
-d “name=张三”
-d “email=vcmq@foxmail.com”
‘http://localhost:8082/learning-validator/create’
返回结果:

{“data”:{“name”:“张三”,“email”:“vcmq@foxmail.com”},“success”:true,“msg”:“success”}
可以看到 id 这个字段在 create 的时候,并没有进行校验。修改为 update 接口进行测试。

curl -i -X POST \
-d “name=张三”
-d “email=vcmq@foxmail.com”
‘http://localhost:8082/learning-validator/update’
返回结果:

{“success”:false,“msg”:“学号不能为空”}
手动校验
除了使用@Validated 注解方式校验,也可以进行手动校验,手动校验同样也支持分组校验。

@PostMapping("/create2")
public Result<StudentVo> create2(StudentVo student) {
  ValidatorUtils.validateEntity(student, CreateGroup.class);
  System.out.println(student.toString());
  return Result.success(student);
}

校验工具类:

public class ValidatorUtils {
  private static Validator validator;

  static {
    validator = Validation.byProvider(HibernateValidator.class)
      .configure()
      // 快速失败
      .failFast(true)
      .buildValidatorFactory().getValidator();
  }

  /**
     * 校验对象
     *
     * @param object 待校验对象
     * @param groups 待校验的组
     * @throws ApiException 校验不通过,则报 ApiException 异常
     */
  public static void validateEntity(Object object, Class<?>... groups)
    throws ApiException {
    Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
    if (!constraintViolations.isEmpty()) {
      constraintViolations.stream().findFirst()
        .map(ConstraintViolation::getMessage)
        .ifPresent(v1 -> {
          throw new ApiException(v1);
        });
    }
  }

常用注解
空与非空检查
注解 支持 Java 类型 说明
@Null Object 为 null
@NotNull Object 不为 null
@NotBlank CharSequence 不为 null,且必须有一个非空格字符
@NotEmpty CharSequence、Collection、Map、Array 不为 null,且不为空(length/size>0)
Boolean 值检查
注解 支持 Java 类型 说明 备注
@AssertTrue boolean、Boolean 为 true 为 null 有效
@AssertFalse boolean、Boolean 为 false 为 null 有效
日期检查
注解 支持 Java 类型 说明 备注
@Future Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate 验证日期为当前时间之后 为 null 有效
@FutureOrPresent Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate 验证日期为当前时间或之后 为 null 有效
@Past Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate 验证日期为当前时间之前 为 null 有效
@PastOrPresent Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate 验证日期为当前时间或之前 为 null 有效
数值检查
注解 支持 Java 类型 说明 备注
@Max BigDecimal、BigInteger,byte、short、int、long 以及包装类 小于或等于 为 null 有效
@Min BigDecimal、BigInteger,byte、short、int、long 以及包装类 大于或等于 为 null 有效
@DecimalMax BigDecimal、BigInteger、CharSequence,byte、short、int、long 以及包装类 小于或等于 为 null 有效
@DecimalMin BigDecimal、BigInteger、CharSequence,byte、short、int、long 以及包装类 大于或等于 为 null 有效
@Negative BigDecimal、BigInteger,byte、short、int、long、float、double 以及包装类 负数 为 null 有效,0 无效
@NegativeOrZero BigDecimal、BigInteger,byte、short、int、long、float、double 以及包装类 负数或零 为 null 有效
@Positive BigDecimal、BigInteger,byte、short、int、long、float、double 以及包装类 正数 为 null 有效,0 无效
@PositiveOrZero BigDecimal、BigInteger,byte、short、int、long、float、double 以及包装类 正数或零 为 null 有效
@Digits(integer = 3, fraction = 2) BigDecimal、BigInteger、CharSequence,byte、short、int、long 以及包装类 整数位数和小数位数上限 为 null 有效
其他
注解 支持 Java 类型 说明 备注
@Pattern CharSequence 匹配指定的正则表达式 为 null 有效
@Email CharSequence 邮箱地址 为 null 有效,默认正则 ‘.*’
@Size CharSequence、Collection、Map、Array 大小范围(length/size>0) 为 null 有效
hibernate-validator 扩展约束(部分)
注解 支持 Java 类型 说明
@Length String 字符串长度范围
@Range 数值类型和 String 指定范围
@URL URL 地址验证

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Hibernate Validator是一个用于Java Bean验证的框架,它提供了一组注解和API,用于验证Java Bean的属性值是否符合指定的规则和约束。它可以用于验证表单数据、REST API请求、数据库实体等各种场景下的数据。Hibernate Validator支持多种验证规则,包括基本数据类型、字符串、日期、集合、数组等。它还支持自定义验证规则和错误消息。使用Hibernate Validator可以有效地减少代码量和提高开发效率。 ### 回答2: Hibernate Validator是一个流行的Java开源校验框架,它是基于JSR 303规范(Bean验证)的实现。它提供了一组注释和API,用于对JavaBean进行验证和校验Hibernate Validator提供了很多内置的校验注释,比如@NotNull、@NotEmpty、@Min、@Max等。这些注释可以直接应用在JavaBean的属性上,通过注释指定的校验规则来验证属性的值是否合法。同时,Hibernate Validator还支持自定义校验注释,可以根据业务需求来定义新的注释,并实现相应的校验逻辑。 Hibernate Validator不仅可以对单个属性进行校验,还支持对整个JavaBean进行校验。例如,可以在JavaBean的类级别上添加@Valid注释,来验证整个对象的合法性。此外,还可以通过分组校验来实现在不同场景下不同的校验规则。 除了注释方式外,Hibernate Validator还提供了一套强大的API,通过编程方式来进行校验和验证。通过ValidatorFactory和Validator两个主要的接口,可以创建Validator对象并执行校验操作。可以验证单个属性的值,也可以验证整个JavaBean对象。 Hibernate Validator还提供了国际化的支持,可以根据不同的区域设置显示不同的错误信息。同时,还能够将校验错误信息与具体的校验注释进行关联,方便开发者快速定位问题。 总结来说,Hibernate Validator提供了一种有效、灵活和方便的方式来对JavaBean进行校验。它的注释和API丰富多样,并且支持自定义校验规则,同时还提供了国际化和错误信息关联等特性,使得校验过程更加强大和可控。它在Java开发中的应用越来越广泛,为开发者提供了一种便捷的校验解决方案。 ### 回答3: Hibernate Validator是一个基于JSR 380规范的校验框架,它可以轻松地对Java对象进行校验。通过使用Hibernate Validator,开发人员可以在应用程序中方便地添加校验规则,并且可以验证这些规则是否被满足。 Hibernate Validator提供了一组注解,这些注解可以附加在JavaBean的属性上,以标识需要进行校验的规则。例如,@NotNull注解用于确保属性的值不为空,@Size注解用于确保字符串类型的属性的长度在指定范围内,等等。除了注解外,Hibernate Validator还提供了一些内置的校验器,用于验证各种数据类型的属性,例如字符串、数字、日期等。 使用Hibernate Validator进行校验非常简单。只需要在需要校验JavaBean上添加注解,并在需要校验的时候调用校验方法即可。校验方法会返回一个包含校验结果的对象,开发人员可以根据需要进行处理。校验方法还可以接受一个可选的校验分组参数,用于校验不同场景下的不同规则。 Hibernate Validator还提供了一些扩展功能,用于自定义校验规则。开发人员可以创建自定义的校验注解,并编写相应的校验器来实现特定的校验逻辑。这使得Hibernate Validator非常灵活,可以满足各种不同的校验需求。 总结来说,Hibernate Validator是一个强大而灵活的校验框架,能够方便地对Java对象进行校验。使用Hibernate Validator可以增加应用程序的稳定性和可靠性,减少错误和异常的发生。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值