介绍
在开发现代应用程序时,数据验证是确保用户输入的正确性和应用程序数据完整性的关键方面。Spring Boot 提供了强大的数据验证机制,使开发者能够轻松地执行验证操作。本文将深入介绍 Spring Boot 中的 Validation,以及如何在应用程序中正确使用它。
为什么使用数据验证?
- 用户输入的正确性:数据验证是确保用户输入的正确性的一种重要手段。通过验证用户输入的数据,可以放置无效或错误的数据进入应用程序,提高数据的质量。例如:系统中的备注字段数据库中对应的长度是256,如果用户输入的备注超过这个长度值,那么就会导致mysql报
Data too long
。 - 数据完整性:数据完整性是指数据在存储和传输过程中的准确性和一致性。数据验证有助于确保数据满足特定的格式、长度、范围等要求,从而提高数据的完整性。
- 安全性:数据验证也是保证应用程序安全性的关键因素。通过验证用户输入,可以防范一些潜在的安全威胁,例如 SQL 注入、跨站脚本攻击等。
- 业务规则的执行:在应用程序中,通常存在一些业务规则,例如某个字段不能为空、日期范围必须在某个特定范围内等。通过数据验证,可以确保这些业务规则在应用程序中得到正确执行。
手动数据校验的痛点
日常开发中,有些项目可能没有采用Spring Validator,采用的是在代码中手动校验数据。但是手动校验数据会带来代码冗余、错误处理的一致性以及业务规则的维护的一些痛点。
- 代码冗余的手动校验逻辑,导致代码中大量的if-else
1
2
3
4
5
6
7
8
9
10
11
12
13public ResponseEntity<String> registerUser(UserRegistrationRequest request) {
if(request == null){
return REsponseEntity.badRequest().body("Request cannot be null");
}
if(StrUtil.isBlankIfStr(request.getUsername())){
return ResponseEntity.badRequest().body("Request cannoy be blank");
}
if(StrUtil.length(request.getPassword()) < 6){
return ResponseEntity.badRequest().body("Password must be at least 6 characters long");
}
//处理用户注册逻辑
return ResponseEntity.ok("User registered successfully");
} - 缺乏统一的错误处理机制
- 随着业务规则的增加,手动编写的校验逻辑可能变得庞大且难以维护。修改和扩展校验规则可能需要修改多个地方,增加了维护成本。
- 缺乏验证组的支持 手动校验通常不支持验证组的概念,难以根据不同场景执行不同的验证规则
- 不易于集成前端验证 手动校验不易与前端验证框架集成,导致前后端验证逻辑可能不一致。
通过引入 Spring Validator,我们能够有效解决这些痛点,提高代码的可读性、可维护性,并确保校验逻辑的一致性。
Spring Boot 中的 Validation 概述
因 springboot 的 spring-boot-satrter-web
默认内置了 Hibernate-Validator
,虽然 Hibernate-Validator
也能做到数据校验,但是考虑到 spring-boot-starter-validation
是一个抽象层,使得验证框架的具体实现变得可插拔。这意味着,除了 Hibernate Validator
,开发者可以选择符合 Bean Validation 规范的实现。所以我们可以手动引入 spring-boot-starter-validation
实现数据验证。
1 | <dependency> |
spring-boot-starter-validation
不仅支持 JSR-303 (Bean validation1.0)规范,还提供了对 JSR-380(Bean Validation2.0)规范的全面支持。这使得开发者可以利用 Bean Validation 2.0 的新特性,更灵活地定义验证规则,包括对集合、嵌套对象的验证等。
通过在实体类的字段上使用标准的 Bean Validation 注解(如 @NotBlank
、@Size
、@Email
等),我们能够直观地定义数据的验证规则。这些验证规则会在应用程序的不同层次(如控制器层)生效,确保输入数据的正确性
基本用法
Spring Boot Validation 提供了一系列注解,用于在实体类中定义验证规则。以下是一些常用的校验相关的注解及其功能以及用法:
@NotNull
:校验元素值不能为null。如果元素为null,则验证失败。通常用于字段级别的验证。
1 |
|
@NotBlank
:校验字符串元素值不能为 null 或空字符串。必须包含至少一个非空格字符(即执行trim()之后不为’’)。如果元素为null或者‘ ’,则验证失败。通常用于String
类型的字段校验
1 |
|
@NotEmpty
:校验集合元素或数组元素或者字符串是否非空。通常作用于集合字段或数组字段,此时需要集合或者数字的元素个数大于0。也可以作用于字符串,此时校验字符串不能为null或空串(可以是一个空格)。注意与@NotBlank
的使用区别。
1 |
|
@Length
:校验字符串元素的长度。作用于字符串。注:Hibernate-Validator
中注解,等同于spring-boot-starter-validation
中的@Size
。
1 |
|
@Size
:校验集合元素个数或字符串的长度在指定范围内。在集合或字符串上添加@Size
注解。
1 |
|
6.@Min
: 校验数字元素的最小值。
1 |
|
7.@Max
: 校验数字元素的最大值。
1 |
|
9.@DecimalMax
: 作用于BigDecimal
类型字段, 校验字段的最大值,支持比较的值为字符串表示的十进制数。通常搭配它的inclusive()
使用,区别边界问题。value
属性表示最大值,inclusive 属性表示是否包含最大值。
1 |
|
10.@DecimalMin
: 作用于BigDecimal
类型字段, 校验字段的最小值,支持比较的值为字符串表示的十进制数。通常搭配它的inclusive()
使用,区别边界问题。value
属性表示最小值,inclusive 属性表示是否包含最小值。
1 |
|
11.@Email
: 校验字符串元素是否为有效的电子邮件地址。可以通过regexp
自定义邮箱匹配正则。
1 |
|
12.@Pattern
: 根据正则表达式校验字符串元素的格式。
1 |
|
13.@Digits
: 校验数字元素的整数部分和小数部分的位数。作用于BigDecimal
,BigInteger
,字符串,以及byte
, short
,int
, long
以及它们的包装类型。
1 |
|
14.@Past
: 校验日期或时间元素是否在当前时间之前。即是否是过去时间。作用于Date相关类型的字段。
1 |
|
15.@Future
: 校验日期或时间元素是否在当前时间之后。即是否是未来时间。作用于Date相关类型的字段。
1 |
|
注:以上只罗列部分注解以及它们的功能,其余他们的字段属性并没有详细说明,其他注解以及详细的说明需要去看源码。
用法示例
1.定义接口入参请求参数
1 | /** |
2.定义请求接口
1 |
|
3.测试
我们需要捕获一下MethodArgumentNotValidException
。该部分内容请参考文章:项目搭建-统一参数校验
嵌套对象的校验
在 UserCreateRequestVO
中增加一个 address
的校验,即需要对嵌套对象进行校验
1 |
|
在UserAddressRequestVO
中增加address
属性
1 |
|
解决办法,要在嵌套对象上使用 @Valid 注解
1 |
|
处理验证错误
由上述测试结果中,可以看出接口抛出的一场结果并不是很友好,我们需要统一的处理一下异常以及返回结果,给予用户友好提示。具体实现,在这里不再赘述,可以移步:项目搭建-统一参数校验
总结
Spring Boot Validation通过简化验证流程、集成Bean Validation规范、支持分组验证以及提供友好的错误处理,为Java应用开发者提供了强大而灵活的数据验证机制。最佳实践包括在控制器层使用@Validated
注解、合理利用各种验证注解、使用自定义验证注解解决特定业务需求,确保代码清晰简洁、符合规范,并提高系统的可维护性和用户体验。