[数据校验/数据质量] 数据校验框架:hibernate-validation
0 前言
- 其一,項(xiàng)目中普遍遇到了此問題,故近兩天深入地研究了一下。
- 其二,能夠自信地說,仔細(xì)看完本篇,就無需再看其他的Java數(shù)據(jù)校驗(yàn)框架的文章了。
1 數(shù)據(jù)校驗(yàn)框架概述
1.0 數(shù)據(jù)校驗(yàn)框架的產(chǎn)生背景
以Web項(xiàng)目為例,用戶需要填寫表單信息保存提交。
頁面輸入信息需要進(jìn)行數(shù)據(jù)格式校驗(yàn),并且返回對(duì)應(yīng)的錯(cuò)誤提示,以此來達(dá)到數(shù)據(jù)校驗(yàn)的目的,從而避免無效數(shù)據(jù)被保存或者提交。
這些檢查工作包括必填項(xiàng)檢查、數(shù)值檢查、長(zhǎng)度檢查、身份證號(hào)碼、手機(jī)號(hào)碼檢查等工作。
當(dāng)請(qǐng)求參數(shù)格式不正確的時(shí)候,需要程序監(jiān)測(cè)到,對(duì)于前后端分離開發(fā)過程中,數(shù)據(jù)校驗(yàn)還需要返回對(duì)應(yīng)的狀態(tài)碼和錯(cuò)誤提示信息。
如果將這些字段校驗(yàn)和業(yè)務(wù)邏輯混合一起寫,則會(huì):
- 代碼極其臃腫,且不容易維護(hù);
- 干擾原有邏輯;
接下來將細(xì)述在服務(wù)器端,如何對(duì)API的數(shù)據(jù)校驗(yàn)處理技術(shù)。
1.1 數(shù)據(jù)校驗(yàn)框架的演變過程
1.1.1 JSR/Java 規(guī)范提案:BeanValidation
-
JSR:Java Specification Requests的縮寫,意思是Java 規(guī)范提案。是指向JCP(Java Community Process) 提出新增一個(gè)標(biāo)準(zhǔn)化技術(shù)規(guī)范的正式請(qǐng)求。
-
任何人都可以提交JSR,以向Java平臺(tái)增添新的API和服務(wù)。JSR已成為Java界的一個(gè)重要標(biāo)準(zhǔn)。
-
Bean Validation 是一個(gè)運(yùn)行時(shí)的數(shù)據(jù)驗(yàn)證框架的標(biāo)準(zhǔn),在驗(yàn)證之后驗(yàn)證的錯(cuò)誤信息會(huì)被馬上返回。
- BeanValidation就是這個(gè)JSR規(guī)范之一。
- 提到JSR,相信有小伙伴想去看下到底是個(gè)啥。可以看到:
- 規(guī)范從JSR 303 到 JSR 380;
- 目前最新規(guī)范是Bean Validation 2.0
- JSR # Bean Validation : https://jcp.org/en/jsr/summary?id=bean+validation
-
JSR303是專家組成員向JCP提交的第1版Bean Validation,即針對(duì)bean數(shù)據(jù)校驗(yàn)提出的一個(gè)規(guī)范,使用注解方式實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)。后面有升級(jí)版本JSR349及JSR380。各個(gè)版本的規(guī)范對(duì)應(yīng)關(guān)系如下:
-
JSR 380(Bean Validation 2.0)
- JSR380伴隨著JAVAEE 8在2017年發(fā)布,完全兼容低版本的JAVA SE,Hibernate實(shí)現(xiàn)版本6.0.1.Final,Apache BVal實(shí)現(xiàn)版本2.0.3(不太確定)
-
JSR 349(Bean Validation 1.1)
- JSR349伴隨著JAVAEE 7在2013年發(fā)布,Hibernate實(shí)現(xiàn)版本5.1.1.Final,Apache BVal實(shí)現(xiàn)版本1.1.2
- 每一個(gè)注解都包含message字段,用于校驗(yàn)失敗時(shí)作為提示信息,特殊的校驗(yàn)注解,如Pattern(正則校驗(yàn)),還可以自己添加正則表達(dá)式。
-
JSR 303(Bean Validation 1.0)
-JSR303伴隨著JAVAEE 6在2009年發(fā)布,Hibernate實(shí)現(xiàn)版本4.3.1.Final,Apache BVal實(shí)現(xiàn)版本0.5
-
JSR 380(Bean Validation 2.0)
- 主流 Bean Validation 規(guī)范,使用 hibernate-validation 的實(shí)現(xiàn)。
- 如果使用 bean validation 2.0 規(guī)范,hibernate-validation 必須選擇6.0.1以上版本
Bean Validation 2.0中包含了22個(gè)注解
- JSR / Bean Valiadation 與 Hibernate Validation、Spring Valiadation
- JSR規(guī)定一些校驗(yàn)規(guī)范即校驗(yàn)注解,如@Null,@NotNull,@Pattern,位于javax.validation.constraints包下,只提供規(guī)范不提供實(shí)現(xiàn)。
- 而hibernate validation是對(duì)這個(gè)規(guī)范的實(shí)踐,提供相應(yīng)的實(shí)現(xiàn),并增加一些其他校驗(yàn)注解,如@Email,@Length,@Range等等,位于org.hibernate.validator.constraints包下。
- spring對(duì)hibernate validation進(jìn)行二次封裝,顯示校驗(yàn)validated bean時(shí),可以使用spring validation或hibernate validation;
- 而spring validation另一特性:便是其在springmvc模塊中添加自動(dòng)校驗(yàn),并將校驗(yàn)信息封裝進(jìn)特定的類中。
1.1.2 JAVAX.VALIDATION.API
- Java 在2009年的 JAVAEE 6 中發(fā)布了 JSR303以及 javax 下的 validation 包內(nèi)容。
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${javax.validation-api.version}</version>
</dependency>
重要版本
javax.validation:validation-api.version = 2.0.1.Final
spring-boot-starter-web/validation:2.1.4.RELEASE | hibernate-alidation:6.0.16.Final 中使用的 validation-api 為:
javax.validation:validation-api:2.0.1.Final
hibernate-alidation:6.0.16.Final 中使用的 validation-api 為:
javax.validation:validation-api:2.0.1.Final
- 這項(xiàng)工作的【主要目標(biāo)】是為java應(yīng)用程序開發(fā)人員提供 :
- 基于java對(duì)象的 約束(constraints)聲明 ;
- 注:每個(gè)約束都有參數(shù) message,groups 和 payload。這是 Bean Validation 規(guī)范的要求。
- 對(duì)約束的驗(yàn)證工具(validator);
- 約束元數(shù)據(jù)存儲(chǔ)庫和查詢API;
- 默認(rèn)實(shí)現(xiàn);
- 基于java對(duì)象的 約束(constraints)聲明 ;
- Java8開始,Java EE 改名為Jakarta EE。
- 故javax.validation相關(guān)的api在jakarta.validation的包下。因此:
- 大家看不同的版本的時(shí)候,會(huì)發(fā)現(xiàn)以前的版本包在
javax.validation包下。 - 最新的版本包在
jakarta.validation包下
- 大家看不同的版本的時(shí)候,會(huì)發(fā)現(xiàn)以前的版本包在
-
javase的支持還在jcp(Java Community Process,Java社區(qū)標(biāo)準(zhǔn)過程),Java EE 改名 JakartaE E。- JakartaEE的官網(wǎng)及其支持的項(xiàng)目:
- https://jakarta.ee/
- JakartaEE的官網(wǎng)及其支持的項(xiàng)目:
- 故javax.validation相關(guān)的api在jakarta.validation的包下。因此:
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>${jakarta.validation-api.version}</version>
</dependency>
重要版本
jakarta.validation:jakarta.validation-api.version = 2.0.2
spring-boot-starter-validation:2.3.12.RELEASE | hibernate-alidation:6.1.7.Final 使用的 validation-api 為:
jakarta.validation:jakarta.validation-api:2.0.2
-
Bean Validation 2.0規(guī)范及默認(rèn)實(shí)現(xiàn):
- https://beanvalidation.org/2.0/spec/#whatsnew
1.1.3 HIBERNATE-VALIDATOR
- hibernate-validator是Hibernate項(xiàng)目中的一個(gè)數(shù)據(jù)校驗(yàn)框架,是Bean Validation 的參考實(shí)現(xiàn);
- 【注意】
- 此處的 Hibernate 不是 Hibernate ORM,二者沒有任何關(guān)系;
- hibernate-validator 和 hibernate orm 項(xiàng)目 均是 Hibernate 基金會(huì)(
org.hibernate)下的項(xiàng)目之一。
- 【注意】
-
hibernate-validator除了提供了JSR 303規(guī)范中所有內(nèi)置constraint 的實(shí)現(xiàn),還有一些附加的constraint。 - 使用hibernate-validator能夠?qū)?strong>數(shù)據(jù)校驗(yàn)從業(yè)務(wù)代碼中脫離出來,增加代碼可讀性;同時(shí)也讓數(shù)據(jù)校驗(yàn)變得更加方便、簡(jiǎn)單。
- 項(xiàng)目官網(wǎng):
-
https://hibernate.org/validator/ |
- 項(xiàng)目官網(wǎng)
-
http://hibernate.org/validator/documentation
- 官方文檔
-
https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/
- hibernate-validator的簡(jiǎn)介
-
https://github.com/hibernate/hibernate-validator
- GitHub 源代碼倉庫
-
https://hibernate.org/validator/ |
- 在Java語言中,
hibernate-validation已成為【數(shù)據(jù)校驗(yàn)框架】實(shí)質(zhì)上的標(biāo)準(zhǔn)框架的標(biāo)準(zhǔn)實(shí)現(xiàn),再無其他框架可望其項(xiàng)背。
spring-boot-starter-web/validation:2.1.4.RELEASE | hibernate-alidation:6.0.16.Final
hibernate-alidation:6.0.16.Final
spring-boot-starter-validation:2.3.12.RELEASE | hibernate-alidation:6.1.7.Final
2 實(shí)踐使用
2.1 基本使用步驟
Step1 編寫實(shí)體類
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
@NotBlank(message = "用戶名不能為空")
private String name;
@NotBlank(message = "郵箱不能為空")
private String email;
}
Step2 引入依賴包(數(shù)據(jù)校驗(yàn)的標(biāo)準(zhǔn)框架及實(shí)現(xiàn))
情況1:基于 javax.validation api
基于
javax.validationapi 的第三方庫的有:
[1] javax.validation:validation-api : 2.0.1.Final
- [1] hibernate-alidation : 6.0.16.Final
- [2] spring-boot-starter-web/validation : 2.1.4.RELEASE
注:本組件依賴的hibernate-validation的組件版本為 6.0.16.Final
<!-- | data validation framework | start -->
<!-- javax.validation-api : data validation api design standard & framework -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${javax.validation-api.version}</version>
</dependency>
<!-- hibernate-validator | http://hibernate.org/validator/documentation-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
<!-- org.glassfish:javax.el | hibernate-validator 依賴此組件,以防報(bào)錯(cuò):"javax.validation.ValidationException: HV000183: Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead" --><dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>${glassfish.javax.el.version}</version>
</dependency>
<!-- | data validation framework | end -->
- 版本變量取值
- javax.validation-api.version = 2.0.1.Final
- hibernate-validator.version = 6.0.16.Final
- glassfish.javax.el.version = 3.0.1-b09
情況2:基于 jakarta.validation api
基于
jakarta.validationapi 的第三方庫的有:
[1] jakarta.validation : jakarta.validation-api : 2.0.2
- [1] hibernate-alidation : 6.1.7.Final
- [2] spring-boot-starter-validation : 2.3.12.RELEASE
注:本組件依賴的hibernate-validation的組件版本為 6.1.7.Final
方式1
<!-- | data validation framework | start -->
<!-- jakarta.validation-api : data validation api design standard & framework -->
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>${jakarta.validation-api.version}</version>
</dependency>
<!-- hibernate-validator | http://hibernate.org/validator/documentation -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
<!-- org.glassfish:javax.el or org.glassfish:jakarta.el [recommend] (2選1即可) | hibernate-validator 依賴此組件,以防報(bào)錯(cuò):"javax.validation.ValidationException: HV000183: Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead" -->
<!--
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>${glassfish.javax.el.version}</version>
</dependency> -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>${glassfish.jakarta.el.version}</version>
</dependency>
<!-- | data validation framework | end -->
- 版本變量取值
- jakarta.validation-api.version = 2.0.2
- hibernate-validator.version = 6.1.7.Final
- glassfish.javax.el.version = 3.0.1-b09 | glassfish.jakarta.el.version = 3.0.3
方式2:直接引用
spring-boot-starter-validation
<!-- | data validation framework | start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- | data validation framework | end -->
版本變量的取值
spring-boot.version := 2.3.12.RELEASE
由上可見:
[1]spring-boot-starter-validation: 2.3.12.RELEASE
依賴hibernate-validation: 6.1.7.Final
依賴org.glasssfish : jakarta.el: 3.0.3
由上可見:
[1]hibernate-validation: 6.1.7.Final
依賴了org.glasssfish : jakarta.el(由于scope是provided,故具體版本未限制)
Step3 編寫數(shù)據(jù)驗(yàn)證代碼
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
//import org.hibernate.validator.HibernateValidator;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Student student = new Student("小明", null);
System.out.println(student);
//方式1
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
//方式2
//ValidatorFactory factory = Validation.byProvider( HibernateValidator.class ).configure()
//.addProperty( "hibernate.validator.fail_fast", "true" ) //true 快速失敗返回模式 | false 普通模式
//.buildValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Student>> violations = validator.validate(student);
for (ConstraintViolation<Student> violation : violations) {
System.out.println(violation.getMessage());
}
}
}
output
Student(name=小明, email=null)
十一月 10, 2023 12:56:58 下午 org.hibernate.validator.internal.util.Version <clinit>
INFO: HV000001: Hibernate Validator 6.0.16.Final
郵箱不能為空
2.2 支持的POJO校驗(yàn)注解
2.2.1 javax/jakarta.validation 注解列表
在要校驗(yàn)的POJO上加上以下注解即可:
- 形如:
javax.validation.constraints.Email
| 注解 | 用途 |
|---|---|
| Valid (最常用的【標(biāo)識(shí)注解】) | 遞歸的對(duì)關(guān)聯(lián)的對(duì)象進(jìn)行校驗(yàn) 標(biāo)記用于驗(yàn)證級(jí)聯(lián)的屬性、方法參數(shù)或方法返回類型;在驗(yàn)證屬性、方法參數(shù)或方法返回類型時(shí),將驗(yàn)證在對(duì)象及其屬性上定義的約束。此行為是遞歸應(yīng)用的。 |
| AssertFalse | 用于boolean字段,該字段的值只能為false |
| AssertTrue | 用于boolean字段,該字段只能為true |
| DecimalMax(value) | 被注釋的元素必須是一個(gè)數(shù)字,只能大于或等于該值 |
| DecimalMin(value) | 被注釋的元素必須是一個(gè)數(shù)字,只能小于或等于該值 |
| Digits(integer,fraction) | 檢查是否是一種數(shù)字的(整數(shù),小數(shù))的位數(shù) |
| Future | 檢查該字段的日期是否是屬于將來的日期 |
| FutureOrPresent | 判斷日期是否是將來或現(xiàn)在日期 |
| Past | 檢查該字段的日期是在過去 |
| PastOrPresent | 判斷日期是否是過去或現(xiàn)在日期 |
| Max(value) | 該字段的值只能小于或等于該值 |
| Min(value) | 該字段的值只能大于或等于該值 |
| Negative | 判斷負(fù)數(shù) |
| NegativeOrZero | 判斷負(fù)數(shù)或0 |
| Positive | 判斷正數(shù) |
| PositiveOrZero | 判斷正數(shù)或0 |
| NotNull | 不能為null |
| Null | 必須為 null |
| Pattern(value) @Pattern(regexp = ) |
被注釋的元素必須符合指定的正則表達(dá)式 |
| Size(max, min) | 檢查該字段的size是否在min和max之間,可以是字符串、數(shù)組、集合、Map等 |
| Length(max, min) | 判斷字符串長(zhǎng)度 |
| CreditCardNumber | 被注釋的字符串必須通過Luhn校驗(yàn)算法,銀行卡,信用卡等號(hào)碼一般都用Luhn計(jì)算合法性 |
| 被注釋的元素必須是電子郵箱地址 | |
| Length(min=, max=) | 被注釋的字符串的大小必須在指定的范圍內(nèi) |
| NotBlank | 只能用于字符串不為null,并且字符串trim()以后length要大于0 |
| NotEmpty | 集合對(duì)象的元素不為0,即集合不為空,也可以用于字符串不為null |
| Range(min=, max=) | 被注釋的元素必須在合適的范圍內(nèi) |
| SafeHtml | classpath中要有jsoup包 |
| ScriptAssert | 要有Java Scripting API 即JSR 223("Scripting for the JavaTMPlatform")的實(shí)現(xiàn) |
| URL(protocol=,host=,port=,regexp=,flags=) | 被注釋的字符串必須是一個(gè)有效的url |
- 注意
- @NotEmpty 用在集合類上面
- @NotBlank 用在String上面
- @NotNull 用在基本類型上
更多功能,如:自定義校驗(yàn)規(guī)則、分組校驗(yàn)、關(guān)聯(lián)參數(shù)聯(lián)合校驗(yàn)請(qǐng)查看官網(wǎng)文檔。
2.2.2 springframework.validation 注解列表
- @Validated(spring) | 最常用的【標(biāo)識(shí)注解】
- 包路徑:
- org.springframework.validation.annotation.Validated
- spring 提供的擴(kuò)展注解,可以方便的用于分組校驗(yàn)
- 其中,message 是提示消息,groups 可以根據(jù)情況來分組。
- 包路徑:
2.2.3 樣例
public class ParamTestDTO implements Serializable {
private static final long serialVersionUID = 7123882542534668217L;
@AssertTrue(message = "Error True")
private Boolean testTrue;
@AssertFalse(message = "Error False")
private Boolean testFalse;
@DecimalMax(value = "10", message = "Error StrMax")
private String testStrMax;
@DecimalMin(value = "1", message = "Error StrMin")
private String testStrMin;
@Max(value = 10, message = "Error Max")
private Integer testMax;
@Min(value = 1, message = "Error Min")
private Double testMin;
@Digits(integer = 2, fraction = 3, message = "Error Dig")
private BigDecimal testDig;
@Past(message = "Error Past")
private Date testPast;
@Future(message = "Error Future")
private Date testFuture;
@Null(message = "Error Null")
private String testNull;
@NotNull(message = "Error NonNull")
private String testNonNull;
@Pattern(regexp = "^[0-9]?[0-9]$", message = "Error Pattern")
private String testPattern;
@Size(min = 1, max = 10, message = "Error Size")
private List<String> testSize;
@Length(min = 1, max = 10, message = "Error Length")
private String testLength;
@NotBlank(message = "Error Blank")
private String testBlank;
@NotEmpty(message = "Error NotEmpty")
private String testEmpty;
@Range(min = 1, max = 10, message = "Error Range")
private String testRange;
}
2.3 應(yīng)用場(chǎng)景
<dubbo:reference id="xxxService" interface="xxx.ValidationService" validation="true" />
- 在服務(wù)器端驗(yàn)證參數(shù)
<dubbo:service interface="xxx.ValidationService" ref="xxxService" validation="true" />
- 在代碼里校驗(yàn)入?yún)?/li>
//obj為包含Hibernate Validator注解的POJO
//快速失敗模式
ValidResult validResult = ValidationUtil.fastFailValidate(obj);
//obj為包含Hibernate Validator注解的POJO
//全部校驗(yàn)?zāi)J?ValidResult validResult = ValidationUtil.allCheckValidate(obj);
2.3.2 場(chǎng)景:Web POST Api Controller
@RestController
public class StudentController {
// ...
@RequestMapping(value = "/addStudent",method = RequestMethod.POST)
public String addStudent(@Valid @RequestBody Student student){
System.out.println("student = [" + student + "]");
return "ok";
}
// ...
}
- 注意
- POST請(qǐng)求必須要加
@Valid
-
@Valid注解:遞歸的對(duì)關(guān)聯(lián)的對(duì)象進(jìn)行校驗(yàn)
- 區(qū)分請(qǐng)求參數(shù):@RequestBody 和 @RequestParam
- @RequestBody 獲取的是請(qǐng)求體里面的數(shù)據(jù),一般是前端傳給后端的JSON字符串。
- @RequestParam 接收的是url里面的查詢參數(shù)(比如xxxxxxx?name=admin)
2.3.3 場(chǎng)景:Web GET Api Controller
import org.springframework.validation.annotation.Validated;
@RestController
@Validated
public class StudentController {
//...
@RequestMapping(value = "/addStudent1",method = RequestMethod.GET)
public String addStudent1(@NotBlank(message = "name不能為空") String name){
System.out.println("name = [" + name + "]");
return "ok addStudent1";
}
//...
}
- Get請(qǐng)求需要在類上添加
@Validated
- org.springframework.validation.annotation.Validated
<dubbo:service interface="xxx.ValidationService" ref="xxxService" validation="true" />
//obj為包含Hibernate Validator注解的POJO
//快速失敗模式
ValidResult validResult = ValidationUtil.fastFailValidate(obj);
//obj為包含Hibernate Validator注解的POJO
//全部校驗(yàn)?zāi)J?ValidResult validResult = ValidationUtil.allCheckValidate(obj);
@RestController
public class StudentController {
// ...
@RequestMapping(value = "/addStudent",method = RequestMethod.POST)
public String addStudent(@Valid @RequestBody Student student){
System.out.println("student = [" + student + "]");
return "ok";
}
// ...
}
- POST請(qǐng)求必須要加
@Valid-
@Valid注解:遞歸的對(duì)關(guān)聯(lián)的對(duì)象進(jìn)行校驗(yàn)
-
- 區(qū)分請(qǐng)求參數(shù):@RequestBody 和 @RequestParam
- @RequestBody 獲取的是請(qǐng)求體里面的數(shù)據(jù),一般是前端傳給后端的JSON字符串。
- @RequestParam 接收的是url里面的查詢參數(shù)(比如xxxxxxx?name=admin)
import org.springframework.validation.annotation.Validated;
@RestController
@Validated
public class StudentController {
//...
@RequestMapping(value = "/addStudent1",method = RequestMethod.GET)
public String addStudent1(@NotBlank(message = "name不能為空") String name){
System.out.println("name = [" + name + "]");
return "ok addStudent1";
}
//...
}
@Validated
- org.springframework.validation.annotation.Validated
2.3.4 場(chǎng)景:優(yōu)雅地返回校驗(yàn)信息
2.3.4.1 定義全局異常處理
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ResultEntity handleBindException(MethodArgumentNotValidException ex) {
FieldError fieldError = ex.getBindingResult().getFieldError();
// 記錄日志。。。
return ResultEntity.faill(211,fieldError.getDefaultMessage(),null);
}
}
2.3.4.2 定義校驗(yàn)失敗返回模板
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResultEntity<T> {
private Integer code;
private String message;
private T data;
public static <T> ResultEntity<T> faill(Integer code,String msg,T t){
return new ResultEntity<T>(code,msg,t);
}
}
2.3.5 場(chǎng)景:對(duì)象級(jí)聯(lián)校驗(yàn)
- Student
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
@NotBlank(message = "用戶名不能為空")
private String name;
@Max(150)
@Min(10)
@NotNull(message = "年齡不能為空")
private Integer age;
@Email
private String email;
@NotNull(message = "user不能為空")
@Valid
private User user;
}
- User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
@NotNull(message = "user對(duì)象中的username不能為空")
private String username;
}
2.3.6 場(chǎng)景:分組校驗(yàn)
如果同一個(gè)類,在不同的使用場(chǎng)景下有不同的校驗(yàn)規(guī)則,那么可以使用分組校驗(yàn)。實(shí)際需求,如未成年人是不能喝酒,如何校驗(yàn)?
public class Foo {
@Min(value = 18, groups = {Adult.class})
private Integer age;
public interface Adult { }
public interface Minor { }
}
@RequestMapping("/drink")
public String drink(@Validated({Foo.Adult.class}) Foo foo, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
for (FieldError item : bindingResult.getFieldErrors()) {
}
return "fail";
}
return "success";
}
2.3.7 場(chǎng)景:自定義校驗(yàn)
作為示例,自定義校驗(yàn)注解@CannotHaveBlank,實(shí)現(xiàn)字符串不能包含空格的校驗(yàn)限制:
- 注解 CannotHaveBlank
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
// 自定義注解中指定這個(gè)注解真正的驗(yàn)證者類
@Constraint(validatedBy = {CannotHaveBlankValidator.class})
public @interface CannotHaveBlank {
// 默認(rèn)錯(cuò)誤消息
String message() default "不能包含空格";
// 分組
Class<?>[] groups() default {};
// 負(fù)載
Class<? extends Payload>[] payload() default {};
// 指定多個(gè)時(shí)使用
@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Documented
@interface List {
CannotHaveBlank[] value();
}
}
- 接口 ConstraintValidator
public interface ConstraintValidator<A extends Annotation, T> {
void initialize(A constraintAnnotation);// 初始化事件方法
boolean isValid(T value, ConstraintValidatorContext context);// 判斷是否合法
}
- 實(shí)現(xiàn)ConstraintValidator接口完成定制校驗(yàn)邏輯的類 : CannotHaveBlankValidator
// 所有的驗(yàn)證者都需要實(shí)現(xiàn)ConstraintValidator接口
public class CannotHaveBlankValidator implements ConstraintValidator<CannotHaveBlank, String> {
@Override
public void initialize(CannotHaveBlank constraintAnnotation) {
//...
}
@Override
// ConstraintValidatorContext包含認(rèn)證中所有的信息,
// 獲取默認(rèn)錯(cuò)誤提示信息,禁用錯(cuò)誤提示信息,改寫錯(cuò)誤提示信息等操作。
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value != null && value.contains(" ")) {
//獲取默認(rèn)提示信息
String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
System.out.println("default message :" + defaultConstraintMessageTemplate);
//禁用默認(rèn)提示信息
context.disableDefaultConstraintViolation();
//設(shè)置提示語
context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation();
return false;
}
return true;
}
}
2.3.8 場(chǎng)景:統(tǒng)一格式化輸出
在驗(yàn)證數(shù)據(jù)時(shí),常常需要給用戶告知錯(cuò)誤信息。通常情況下,錯(cuò)誤信息都是非常簡(jiǎn)短的。為了更好的告知用戶錯(cuò)誤信息,validation-api提供了一種非常好的機(jī)制來格式化錯(cuò)誤信息。
以一個(gè)使用validation-api對(duì)錯(cuò)誤信息進(jìn)行格式化為例子:
public static String validateAndFormat(T obj) {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set> constraintViolationSet = validator.validate(obj);
if (constraintViolationSet != null && constraintViolationSet.size() > 0) {
StringBuilder sb = new StringBuilder();
for (ConstraintViolation constraintViolation : constraintViolationSet) {
sb.append(constraintViolation.getPropertyPath()).append(":").append(constraintViolation.getMessage()).append(",");
}
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
} else {
return "";
}
}
首先使用validator.validate(obj)方法對(duì)數(shù)據(jù)進(jìn)行驗(yàn)證;
如果有錯(cuò)誤信息,則用StringBuilder將錯(cuò)誤信息格式化后返回。
2.4.1 普通模式
默認(rèn)模式
- 首先,校驗(yàn)完所有的屬性;
- 然后,返回所有的驗(yàn)證失敗信息。
2.4.2 快速失敗返回模式
只要有一個(gè)失敗就立馬返回
- 開啟快速失敗返回模式
@Configuration
public class HibernateValidatorConfiguration {
@Bean
public Validator validator(){
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
// true 快速失敗返回模式 false 普通模式
.addProperty( "hibernate.validator.fail_fast", "true" )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
}
測(cè)試驗(yàn)證不通過就會(huì)拋出 ConstraintViolationException 異常,和之前普通模式下拋出的異常不一樣。
所以,為了格式統(tǒng)一還需要自定義的異常處理。
2.4.3 全局異常處理
// 開啟快速失敗返回模式,GET請(qǐng)求校驗(yàn)不通過會(huì)拋出如下異常,在這對(duì)它處理
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public ResultEntity handle(ValidationException exception) {
if (exception instanceof ConstraintViolationException) {
ConstraintViolationException exs = (ConstraintViolationException) exception;
Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
for (ConstraintViolation<?> item : violations) {
System.out.println(item.getMessage());
return ResultEntity.faill(212, item.getMessage(), null);
}
}
return ResultEntity.faill(212, "abc", null);
}
X 參考與推薦文獻(xiàn)
- JSR # Bean Validation :
- https://jcp.org/en/jsr/summary?id=bean+validation
- https://github.com/beanvalidation/beanvalidation-api
- Jakarta (Java EE)
- https://jakarta.ee/
- https://search.maven.org/artifact/jakarta.validation/jakarta.validation-api
- 項(xiàng)目官網(wǎng):
-
https://hibernate.org/validator/ 【推薦】
- 項(xiàng)目官網(wǎng)
-
http://hibernate.org/validator/documentation 【推薦】
- 官方文檔
-
https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/
- hibernate-validator的簡(jiǎn)介
-
https://github.com/hibernate/hibernate-validator
- GitHub 源代碼倉庫
-
https://hibernate.org/validator/ 【推薦】
- Hibernate-Validator(數(shù)據(jù)校驗(yàn)框架) - CSDN
- Hibernate Validator—更簡(jiǎn)潔的參數(shù)校驗(yàn)及一個(gè)util - segmentfault
- Hibernate validation "Unable to initialize javax.el.ExpressionFactory" error - *
-
SPRINGBOOT項(xiàng)目后端表單驗(yàn)證(JAVAX.VALIDATION.API與HIBERNATE-VALIDATOR) - 灰信網(wǎng) 【推薦】
- 在springboot項(xiàng)目中,結(jié)合springAOP和AspectJ,實(shí)現(xiàn)通過自定義注解(
@BeanValidation) + 自定義注解處理方法(Object validateParamByAnnotation(ProceedingJoinPoint ponit, BeanValidation beanValidation))的方式進(jìn)行數(shù)據(jù)格式驗(yàn)證。
- 在springboot項(xiàng)目中,結(jié)合springAOP和AspectJ,實(shí)現(xiàn)通過自定義注解(
- 數(shù)據(jù)校驗(yàn)validation - CSDN
- 【深度思考】如何優(yōu)雅的校驗(yàn)參數(shù)? - Zhihu
總結(jié)
以上是生活随笔為你收集整理的[数据校验/数据质量] 数据校验框架:hibernate-validation的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET周刊【11月第1期 2023-1
- 下一篇: Ansible自动化部署工具-role模