[資料校驗/資料質量] 資料校驗框架:hibernate-validation

2023-11-10 18:00:33

0 前言

  • 其一,專案中普遍遇到了此問題,故近兩天深入地研究了一下。
  • 其二,能夠自信地說,仔細看完本篇,就無需再看其他的Java資料校驗框架的文章了。

1 資料校驗框架概述

1.0 資料校驗框架的產生背景

以Web專案為例,使用者需要填寫表單資訊儲存提交。
頁面輸入資訊需要進行資料格式校驗,並且返回對應的錯誤提示,以此來達到資料校驗的目的,從而避免無效資料被儲存或者提交。
這些檢查工作包括必填項檢查、數值檢查、長度檢查、身份證號碼、手機號碼檢查等工作。

當請求引數格式不正確的時候,需要程式監測到,對於前後端分離開發過程中,資料校驗還需要返回對應的狀態碼和錯誤提示資訊。

如果將這些欄位校驗和業務邏輯混合一起寫,則會:

  • 程式碼極其臃腫,且不容易維護;
  • 干擾原有邏輯;

接下來將細述在伺服器端,如何對API的資料校驗處理技術。

1.1 資料校驗框架的演變過程

1.1.0 原始階段:逐個欄位、逐個物件進行寫死校驗

略,參見 1.0 章節

1.1.1 JSR/Java 規範提案:BeanValidation

  • JSR:Java Specification Requests的縮寫,意思是Java 規範提案。是指向JCP(Java Community Process) 提出新增一個標準化技術規範的正式請求。

  • 任何人都可以提交JSR,以向Java平臺增添新的API和服務。JSR已成為Java界的一個重要標準

  • Bean Validation 是一個執行時的資料驗證框架的標準,在驗證之後驗證的錯誤資訊會被馬上返回。

    • BeanValidation就是這個JSR規範之一。
    • 提到JSR,相信有小夥伴想去看下到底是個啥。可以看到:
      • 規範從JSR 303JSR 380
      • 目前最新規範是Bean Validation 2.0
    • JSR # Bean Validation : https://jcp.org/en/jsr/summary?id=bean+validation

  • JSR303是專家組成員向JCP提交的第1版Bean Validation,即針對bean資料校驗提出的一個規範,使用註解方式實現資料校驗。後面有升級版本JSR349JSR380。各個版本的規範對應關係如下:
    • JSR 380(Bean Validation 2.0)
      • JSR380伴隨著JAVAEE 8在2017年釋出,完全相容低版本的JAVA SE,Hibernate實現版本6.0.1.Final,Apache BVal實現版本2.0.3(不太確定)
    • JSR 349(Bean Validation 1.1)
      • JSR349伴隨著JAVAEE 7在2013年釋出,Hibernate實現版本5.1.1.Final,Apache BVal實現版本1.1.2
      • 每一個註解都包含message欄位,用於校驗失敗時作為提示資訊,特殊的校驗註解,如Pattern(正則校驗),還可以自己新增正規表示式。
    • JSR 303(Bean Validation 1.0)
      • JSR303伴隨著JAVAEE 6在2009年釋出,Hibernate實現版本4.3.1.Final,Apache BVal實現版本0.5

  • 主流 Bean Validation 規範,使用 hibernate-validation 的實現。
    • 如果使用 bean validation 2.0 規範,hibernate-validation 必須選擇6.0.1以上版本

Bean Validation 2.0中包含了22個註解

  • JSR / Bean Valiadation 與 Hibernate Validation、Spring Valiadation
    • JSR規定一些校驗規範即校驗註解,如@Null,@NotNull,@Pattern,位於javax.validation.constraints包下,只提供規範不提供實現。
    • 而hibernate validation是對這個規範的實踐,提供相應的實現,並增加一些其他校驗註解,如@Email,@Length,@Range等等,位於org.hibernate.validator.constraints包下。
    • spring對hibernate validation進行二次封裝,顯示校驗validated bean時,可以使用spring validation或hibernate validation;
      • 而spring validation另一特性:便是其在springmvc模組中新增自動校驗,並將校驗資訊封裝進特定的類中。

1.1.2 JAVAX.VALIDATION.API

  • Java 在2009年的 JAVAEE 6 中釋出了 JSR303以及 javax 下的 validation 包內容。
<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

  • 這項工作的【主要目標】是為java應用程式開發人員提供 :
    • 基於java物件的 約束(constraints)宣告
      • 注:每個約束都有引數 message,groups 和 payload。這是 Bean Validation 規範的要求。
    • 對約束的驗證工具(validator)
    • 約束後設資料儲存庫查詢API
    • 預設實現
  • Java8開始,Java EE 改名為Jakarta EE
    • javax.validation相關的api在jakarta.validation的包下。因此:
      • 大家看不同的版本的時候,會發現以前的版本包javax.validation包下。
      • 最新的版本包在jakarta.validation 包下
    • javase的支援還在jcp(Java Community Process,Java社群標準過程),Java EE 改名 JakartaE E。

<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

1.1.3 HIBERNATE-VALIDATOR

  • hibernate-validator是Hibernate專案中的一個資料校驗框架,是Bean Validation參考實現
    • 【注意】
      • 此處的 Hibernate 不是 Hibernate ORM,二者沒有任何關係;
      • hibernate-validator 和 hibernate orm 專案 均是 Hibernate 基金會(org.hibernate)下的專案之一。
  • hibernate-validator除了提供了JSR 303規範所有內建constraint 的實現,還有一些附加的constraint
  • 使用hibernate-validator能夠將資料校驗業務程式碼中脫離出來,增加程式碼可讀性;同時也讓資料校驗變得更加方便、簡單。
  • 專案官網:
  • 在Java語言中,hibernate-validation已成為【資料校驗框架】實質上的標準框架標準實現,再無其他框架可望其項背。

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 實踐使用

2.1 基本使用步驟

Step1 編寫實體類

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 引入依賴包(資料校驗的標準框架及實現)

情況1:基於 javax.validation api

基於 javax.validation api 的第三方庫的有:
[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 依賴此元件,以防報錯:"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.validation api 的第三方庫的有:
[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 依賴此元件,以防報錯:"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 (由於scopeprovided,故具體版本未限制)

Step3 編寫資料驗證程式碼

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校驗註解

2.2.1 javax/jakarta.validation 註解列表

在要校驗的POJO上加上以下註解即可:

  • 形如:
    • javax.validation.constraints.Email
註解 用途
Valid (最常用的【標識註解】) 遞迴的對關聯的物件進行校驗
標記用於驗證級聯的屬性、方法引數或方法返回型別;在驗證屬性、方法引數或方法返回型別時,將驗證在物件及其屬性上定義的約束。此行為是遞迴應用的。
AssertFalse 用於boolean欄位,該欄位的值只能為false
AssertTrue 用於boolean欄位,該欄位只能為true
DecimalMax(value) 被註釋的元素必須是一個數位,只能大於或等於該值
DecimalMin(value) 被註釋的元素必須是一個數位,只能小於或等於該值
Digits(integer,fraction) 檢查是否是一種數位的(整數,小數)的位數
Future 檢查該欄位的日期是否是屬於將來的日期
FutureOrPresent 判斷日期是否是將來或現在日期
Past 檢查該欄位的日期是在過去
PastOrPresent 判斷日期是否是過去或現在日期
Max(value) 該欄位的值只能小於或等於該值
Min(value) 該欄位的值只能大於或等於該值
Negative 判斷負數
NegativeOrZero 判斷負數或0
Positive 判斷正數
PositiveOrZero 判斷正數或0
NotNull 不能為null
Null 必須為 null
Pattern(value)
@Pattern(regexp = )
被註釋的元素必須符合指定的正規表示式
Size(max, min) 檢查該欄位的size是否在min和max之間,可以是字串、陣列、集合、Map等
Length(max, min) 判斷字串長度
CreditCardNumber 被註釋的字串必須通過Luhn校驗演演算法,銀行卡,信用卡等號碼一般都用Luhn計算合法性
Email 被註釋的元素必須是電子郵箱地址
Length(min=, max=) 被註釋的字串的大小必須在指定的範圍內
NotBlank 只能用於字串不為null,並且字串trim()以後length要大於0
NotEmpty 集合物件的元素不為0,即集合不為空,也可以用於字串不為null
Range(min=, max=) 被註釋的元素必須在合適的範圍內
SafeHtml classpath中要有jsoup包
ScriptAssert 要有Java Scripting API 即JSR 223("Scripting for the JavaTMPlatform")的實現
URL(protocol=,host=,port=,regexp=,flags=) 被註釋的字串必須是一個有效的url
  • 注意
    • @NotEmpty 用在集合類上面
    • @NotBlank 用在String上面
    • @NotNull 用在基本型別上

更多功能,如:自定義校驗規則、分組校驗、關聯引數聯合校驗請檢視官網檔案。

2.2.2 springframework.validation 註解列表

  • @Validated(spring) | 最常用的【標識註解】
    • 包路徑:
      • org.springframework.validation.annotation.Validated
    • spring 提供的擴充套件註解,可以方便的用於分組校驗
    • 其中,message 是提示訊息,groups 可以根據情況來分組。

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 應用場景

2.3.1 場景:Dubbo中使用Hibernate Validator校驗入參

無需util,Dubbo介面設定上的validation為true即可。

  • 在使用者端驗證引數
<dubbo:reference id="xxxService" interface="xxx.ValidationService" validation="true" />
  • 在伺服器端驗證引數
<dubbo:service interface="xxx.ValidationService" ref="xxxService" validation="true" />
  • 在程式碼裡校驗入參
//obj為包含Hibernate Validator註解的POJO
//快速失敗模式
ValidResult validResult = ValidationUtil.fastFailValidate(obj);
//obj為包含Hibernate Validator註解的POJO
//全部校驗模式
ValidResult validResult = ValidationUtil.allCheckValidate(obj);

2.3.2 場景: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請求必須要加@Valid
      • @Valid註解:遞迴的對關聯的物件進行校驗
    • 區分請求引數:@RequestBody 和 @RequestParam
      • @RequestBody 獲取的是請求體裡面的資料,一般是前端傳給後端的JSON字串。
      • @RequestParam 接收的是url裡面的查詢引數(比如xxxxxxx?name=admin)

2.3.3 場景: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請求需要在類上新增 @Validated
    • org.springframework.validation.annotation.Validated

2.3.4 場景:優雅地返回校驗資訊

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 定義校驗失敗返回模板

@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 場景:物件級聯校驗

  • 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物件中的username不能為空")
    private String username;
    
}

2.3.6 場景:分組校驗

如果同一個類,在不同的使用場景下有不同的校驗規則,那麼可以使用分組校驗。實際需求,如未成年人是不能喝酒,如何校驗?

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 場景:自定義校驗

作為範例,自定義校驗註解@CannotHaveBlank,實現字串不能包含空格的校驗限制:

  • 註解 CannotHaveBlank
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
// 自定義註解中指定這個註解真正的驗證者類
@Constraint(validatedBy = {CannotHaveBlankValidator.class})
public @interface CannotHaveBlank {
   // 預設錯誤訊息
   String message() default "不能包含空格";
   // 分組
   Class<?>[] groups() default {};
   // 負載
   Class<? extends Payload>[] payload() default {};
   // 指定多個時使用
   @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);// 判斷是否合法
}
  • 實現ConstraintValidator介面完成客製化校驗邏輯的類 : CannotHaveBlankValidator
// 所有的驗證者都需要實現ConstraintValidator介面
public class CannotHaveBlankValidator implements ConstraintValidator<CannotHaveBlank, String> {
	@Override
	public void initialize(CannotHaveBlank constraintAnnotation) {
		//...
	}
	
	@Override
	// ConstraintValidatorContext包含認證中所有的資訊,
	// 獲取預設錯誤提示資訊,禁用錯誤提示資訊,改寫錯誤提示資訊等操作。
	public boolean isValid(String value, ConstraintValidatorContext context) {
	    if (value != null && value.contains(" ")) {
	        //獲取預設提示資訊
	        String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
	        System.out.println("default message :" + defaultConstraintMessageTemplate);
	        //禁用預設提示資訊
	        context.disableDefaultConstraintViolation();
	        //設定提示語
	        context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation();
	        return false;
	    }
	    return true;
	}
}

2.3.8 場景:統一格式化輸出

在驗證資料時,常常需要給使用者告知錯誤資訊。通常情況下,錯誤資訊都是非常簡短的。為了更好的告知使用者錯誤資訊,validation-api提供了一種非常好的機制來格式化錯誤資訊。

以一個使用validation-api對錯誤資訊進行格式化為例子:

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)方法對資料進行驗證;
如果有錯誤資訊,則用StringBuilder將錯誤資訊格式化後返回。

2.4 小結:Hibernate-Validator 校驗模式

2.4.1 普通模式

預設模式

  • 首先,校驗完所有的屬性;
  • 然後,返回所有的驗證失敗資訊。

2.4.2 快速失敗返回模式

只要有一個失敗就立馬返回

  • 開啟快速失敗返回模式
@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;
    }
}

測試驗證不通過就會丟擲 ConstraintViolationException 異常,和之前普通模式下丟擲的異常不一樣。
所以,為了格式統一還需要自定義的例外處理

2.4.3 全域性例外處理

    // 開啟快速失敗返回模式,GET請求校驗不通過會丟擲如下異常,在這對它處理
    @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 參考與推薦文獻