文章目錄
- 一、JSR 303后臺數據校驗
- 1.1 什么是 JSR303?
- 1.2 為什么使用 JSR 303?
- 二、Spring Boot 中使用數據校驗
- 2.1 基本注解校驗
- 2.1.1 使用步驟
- 2.1.2 舉例
- @Valid注解
- 全局統一異常處理
- 2.2 分組校驗
- 2.2.1 使用步驟
- 2.2.2 舉例
- @Validated注解
- @Validated和@Valid的區別
- 2.3 自定義校驗注解
- 2.3.1 使用步驟
- 2.3.2 舉例
- 2.4 相關注解
- 2.4.1 空檢查相關注解
- 2.4.2 長度檢查
- 2.4.3 布爾值檢查
- 2.4.4 日期檢查
- 2.4.5 數值檢查
- 2.4.6 其他
一、JSR 303后臺數據校驗
1.1 什么是 JSR303?
JSR 是 Java Specification Requests 的縮寫,即 Java 規范提案。存在各種各樣的 JSR,簡單的理解為 JSR 是一種 Java 標準。JSR 303 是其中數據檢驗的一個標準(Bean Validation 1.0 (JSR 303))。
1.2 為什么使用 JSR 303?
-
處理一段業務邏輯,首先要確保數據輸入的正確性,所以需要先對數據進行檢查,保證數據在語義上的正確性,再根據數據進行下一步的處理。
-
前端可以通過 js 程序校驗數據是否合法,后端同樣也需要進行校驗。而后端最簡單的實現就是直接在業務方法中對數據進行處理,但是不同的業務方法可能會出現同樣的校驗操作,這樣就出現了數據的冗余。
-
為了解決這個情況,JSR 303 出現了。JSR 303 使用 Bean Validation,即在 Bean 上添加相應的注解,去實現數據校驗。這樣在執行業務方法前,都會根據注解對數據進行校驗,從而減少自定義的校驗邏輯,減少代碼冗余。
二、Spring Boot 中使用數據校驗
2.1 基本注解校驗
之前在 Spring MVC 中介紹了數據校驗,也例舉了常用的注解。但是使注解生效必須要在 springmvc.xml 中配置,假如沒有配置文件(比如在 spring boot)中怎么辦?----> 下面詳細介紹
spring boot 中需要引入依賴:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.1.1 使用步驟
1、在相關的 Bean 上標注需要處理的注解,并指定需要提示的信息(若不指定,會從默認配置文件中讀取默認的信息)。
2、在相關的方法上,使用 @Valid 注解(或者 @Validated 指定組名)標記需要被校驗的數據,否則會不生效。
注意:檢測到數據異常后,系統會向外拋出異常,如果做了統一異常處理,可以根據 postman 測試的結果,找到控制臺打印出的相應的異常,并處理。
3、處理異常。
-
使用 BindingResult 處理;
-
也可以使用 全局統一異常 處理(@RestControllerAdvice 與 @ExceptionHandler)
全局統一異常處理后續會講
2.1.2 舉例
1、在相關的 Bean 上標注注解,并寫上指定信息。
@Data
public class Emp {@NotNull(message = "id 不能為 null")private Integer id;@NotNull(message = "name 不能為 null")private String name;
}
@Valid注解
2、Controller層中使用 @Valid 注解標記需要檢測的數據。
@RestController
public class EmpController {@PostMapping("/emp")public String createEmp(@Valid @RequestBody Emp emp) {return "ok";}
}
3、測試時,假如不傳 id、name,會拋出 MethodArgumentNotValidException 異常。使用 BindingResult 可以處理異常信息,但 通常使用統一異常處理。
① 使用 BindingResult:
@RestController
public class EmpController {@PostMapping("/emp")public Map createEmp(@Valid @RequestBody Emp emp, BindingResult result) {if (result.hasErrors()) {Map<String, String> map = new HashMap<>();// 獲取校驗結果,遍歷獲取捕獲到的每個校驗結果result.getFieldErrors().forEach(item -> {// 獲取校驗的信息String message = item.getDefaultMessage(); // 也獲取message的值String field = item.getField(); //獲取屬性名// 存儲得到的校驗結果map.put(field, message);});return map;}return "ok";}
}
問題:通過上面的步驟,已經可以捕獲異常、處理異常,但是每次都是在業務方法中手動處理邏輯,這樣的實現,代碼肯定會冗余。可以將其抽出,使用 統一異常處理,每次異常發生時,將其捕獲。
全局統一異常處理
② 全局統一異常處理:@RestControllerAdvice、@ExceptionHandler
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(MethodArgumentNotValidException.class)public Map handlerValidException(MethodArgumentNotValidException e) {BindingResult result = e.getBindingResult();Map<String, String> map = new HashMap<>();// 獲取校驗結果,遍歷獲取捕獲到的每個校驗結果result.getFieldErrors().forEach(item ->{// 存儲得到的校驗結果map.put(item.getField(), item.getDefaultMessage());});return map;}
}
Controller 中不需要再用 BindingResult 去處理數據了。
2.2 分組校驗
1、為什么使用 分組校驗?
上面的過程,如果出現多個方法,都需要校驗 Bean,且校驗規則不同的時候,怎么辦呢?分組校驗就可以去解決該問題,每個分組指定不同的校驗規則,不同的方法執行不同的分組,就可以得到不同的校驗結果。
2、JSR 303 的每個注解都默認具備三個屬性:
- message 用來定義數據校驗失敗后的提示消息,默認讀取配置文件的內容。idea 全局搜索
ValidationMessages.properties
,可以看到默認的信息。 - groups 用來定義分組,它是一個 class 數組,可以指定多個分組。
- payload()
String message() default "{javax.validation.constraints.NotNull.message}";Class<?>[] groups() default { };Class<? extends Payload>[] payload() default { };
2.2.1 使用步驟
1、定義一個空接口,用于指定分組,內部不需要任何實現。
2、指定 注解時,通過 groups 指定分組。用于指定在某個分組條件下,才去執行校驗規則。
3、在 Controller 中通過 @Validated 注解指定分組,去指定校驗。
注:使用分組校驗后,Bean 注解上若不指定分組,則不會執行校驗規則。
2.2.2 舉例
1、如:創建兩個分組接口 AddGroup、UpdateGroup。
AddGroup 用于指定 添加數據 時的校驗規則(比如:id、name 均不為 null)。
UpdateGroup 用于指定 修改數據 時的校驗規則(比如:name 不允許為 null)。
2、給 Bean 添加注解,并指定分組信息。
@Data
public class Emp {@NotNull(message = "id 不能為 null", groups = {AddGroup.class})private Integer id;@NotNull(message = "name 不能為 null", groups = {AddGroup.class, UpdateGroup.class})private String name;
}
@Validated注解
3、通過 @Validated 注解指定分組,去指定校驗
@RestController
public class EmpController {@PostMapping("/emp")public void createEmp(@Validated({AddGroup.class}) @RequestBody Emp emp) {}@PutMapping("/emp")public void UpdateEmp(@Validated({UpdateGroup.class}) @RequestBody Emp emp) {}
}
@Validated和@Valid的區別
-
@Validated:
- Spring 框架特有的注解,是標準 JSR-303 的一個變種,提供了一個分組功能。
- 作用在類上、方法上、方法參數上,不能作用于成員屬性上。
-
@Valid:
-
標準 JSR-303 規范的標記型注解。
-
作用在方法、構造函數、方法參數、成員屬性上。
-
2.3 自定義校驗注解
上面的注解滿足不了業務需求時,可以自定義校驗注解、然后自定義校驗規則。
2.3.1 使用步驟
1、自定義一個校驗注解。可以創建一個 ValidationMessages.properties 用于保存默認的 message 信息。
2、自定義一個校驗器(即自定義校驗規則):實現 ConstraintValidator 接口,并重寫相關方法。
-
initialize :初始化,可以獲取 自定義的屬性的值。
-
isValid :校驗,可以獲取到實際的值,然后與自定義的屬性值進行比較。
3、將校驗注解 與 校驗器 關聯起來。@Constraint(validatedBy = {校驗器類.class})
2.3.2 舉例
自定義一個校驗規則,判斷數據長度是否合法。
1、自定義一個校驗注解:(比如這里是@TestValid)
@Target({FIELD})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy={JiaoYan.class})
public @interface TestValid {// 提示信息String message() default "{本自定義注解的全類名.message}";class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};/*** 返回一個長度* @return 默認為 5*/int length() default 5;
}
配置文件內容為:
自定義注解的全類名.message=值不能為 Null,且長度不超過 5
2、自定義一個校驗器(比如這里是 JiaoYan)
/*** 實現 ConstraintValidator 接口,* 其中 ConstraintValidator 的泛型,一個需要指定自定義的注解,一個需要指定需要獲取的值的類型。* 比如:* ConstraintValidator<TestValid, String> 中* TestValid 表示自定義注解* String 表示獲取的值的類型* 即定義規則,判斷一個 String 的值的長度是否滿足條件*/
public class JiaoYan implements ConstraintValidator<TestValid, String> {/*** 用于保存自定義的(默認)長度*/private int length;/*** 初始化方法,獲取默認數據* @param test 注解對象*/@Overridepublic void initialize(TestValid test) {length = test.length();}/*** 自定義校驗規則,如果 String 為 Null 或者 長度大于 5,則校驗失敗(返回 false)* @param value 需要校驗的值* @param context* @return true 表示校驗成功,false 表示校驗失敗*/@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {return value == null ? false : length > value.length();}
}
3、使用注解
@Data
public class Emp {@NotNull(message = "id 不能為 null", groups = {AddGroup.class})private Integer id;// 默認@TestValid()@NotNull(message = "name 不能為 null", groups = {AddGroup.class, UpdateGroup.class})private String name;// 自定義@TestValid(length = 10, message = "值不能為 Null 且長度不超過 10", groups = {AddGroup.class})private String email;
}
2.4 相關注解
2.4.1 空檢查相關注解
注解 | 注解詳情 |
---|---|
@Null | 被指定的注解元素必須為 Null |
@NotNull | 任意類型,不能為 Null,但可以為空,比如:空數組[]、空字符串"" |
@NotBlank | 針對字符串,不能為 Null,且去除前后空格后的字符串長度要大于 0 |
@NotEmpty | 針對字符串、集合、數組,針對字符串時,不能為 Null,且長度要大于 0 |
2.4.2 長度檢查
注解 | 注解詳情 |
---|---|
@Size | 針對字符串、集合、數組,判斷長度是否在給定范圍內 |
@Length | 針對字符串,判斷長度是否在給定范圍內 |
2.4.3 布爾值檢查
注解 | 注解詳情 |
---|---|
@AssertTrue | 針對布爾值,用來判斷布爾值是否為 true |
@AssertFalse | 針對布爾值,用來判斷布爾值是否為 false |
2.4.4 日期檢查
注解 | 注解詳情 |
---|---|
@Past | 針對日期,用來判斷當前日期是否為 過去的日期 |
@Future | 針對日期,用來判斷當前日期是否為 未來的日期 |
2.4.5 數值檢查
注解 | 注解詳情 |
---|---|
@Max(value) | 針對字符串、數值,用來判斷是否小于等于某個指定值 |
@Min(value) | 針對字符串、數值,用來判斷是否大于等于某個指定值 |
2.4.6 其他
注解 | 注解詳情 |
---|---|
@Pattern | 驗證字符串是否滿足正則表達式 |
@Email | 驗證字符串是否滿足郵件格式 |
@Url | 驗證是否滿足 url 格式 |
@Digits | 驗證數字整數和小數位數,如:@Digits(integer=6, fraction=2) |
文章結束!恭喜你又學會了一個知識點!!!