JSR 303 進行後臺數據校驗

来源:https://www.cnblogs.com/l-y-h/archive/2020/04/28/12797809.html
-Advertisement-
Play Games

一、JSR 303 1、什麼是 JSR 303? JSR 是 Java Specification Requests 的縮寫,即 Java 規範提案。 存在各種各樣的 JSR,簡單的理解為 JSR 是一種 Java 標準。 JSR 303 就是數據檢驗的一個標準(Bean Validation (J ...


一、JSR 303

1、什麼是 JSR 303?

  JSR 是 Java Specification Requests 的縮寫,即 Java 規範提案。
  存在各種各樣的 JSR,簡單的理解為 JSR 是一種 Java 標準。
  JSR 303 就是數據檢驗的一個標準(Bean Validation (JSR 303))。
參考:
  https://www.jianshu.com/p/554533f88370

2、為什麼使用 JSR 303?

  處理一段業務邏輯,首先要確保數據輸入的正確性,所以需要先對數據進行檢查,保證數據在語義上的正確性,再根據數據進行下一步的處理。
  前端可以通過 js 程式校驗數據是否合法,後端同樣也需要進行校驗。而後端最簡單的實現就是直接在業務方法中對數據進行處理,但是不同的業務方法可能會出現同樣的校驗操作,這樣就出現了數據的冗餘。為瞭解決這個情況,JSR 303 出現了。
  JSR 303 使用 Bean Validation,即在 Bean 上添加相應的註解,去實現數據校驗。這樣在執行業務方法前,都會根據註解對數據進行校驗,從而減少自定義的校驗邏輯,減少代碼冗餘。

3、JSR 303 常見操作?

(1)可以通過簡單的註解校驗 Bean 屬性,比如 @NotNull、@Null 等。
(2)可以通過 Group 分組自定義需要執行校驗的屬性。
(3)可以自定義註解並指定校驗規則。
(4)支持基於 JSR 303 的實現,比如 Hibernate Validator(額外添加一些註解)。

 

二、演示 JSR303 的簡單使用

1、構建一個 SpringBoot 項目用來演示

(1)構建一個 SpringBoot 項目,以及使用 EasyCode 逆向生成相關的代碼。
參考地址:
  https://www.cnblogs.com/l-y-h/p/12781586.html
模板代碼地址:
  https://gitee.com/lyh-man/fast-template.git

(2)工具使用詳情:
  SpringBoot 2.2.6 + JDK 1.8 + mysql 1.8 搭建基本開發環境
  IDEA + EasyCode + Lombok 插件 逆向生成基本代碼
  Postman 發送請求,測試介面

2、未使用 JSR303 相關註解時

  沒用 JSR 303 相關註解時,需要手動在業務方法里寫處理數據的邏輯。
  修改 Controller ,簡單測試一下未使用 JSR 303 相關註解時的做法。

@RestController
@RequestMapping("api")
public class EmpController {
    @Resource
    private EmpService empService;

    @PostMapping("/emp")
    public Result createEmp(@RequestBody Emp emp) {
        if (emp.getId() == null || emp.getName() == null) {
            return Result.error().message("數據不存在");
        }
        return Result.ok().data("items", emp).message("數據插入成功");
    }
}

 

 

 

使用 postman 測試該介面,當 id 不存在時,會被檢測到。

 

 

 

id,name 都存在時,不會被捕獲。

 

 

 

  這裡只是簡單的測試一下邏輯,真實的數據檢測肯定比這複雜的多,然後每個方法都需要寫不同的數據處理邏輯,這樣就會造成數據的冗餘。而使用 JSR303 的相關註解,就很簡單,繼續往下看。

 

3、使用 JSR 303 相關註解處理邏輯

(1)使用步驟:
Step1:
  在相關的 Bean 上標註需要處理的註解,並指定需要提示的信息(若不指定,會從預設配置文件中讀取預設的信息)。

Step2:
  在相關的方法上,使用 @Valid 註解(或者 @Validated 指定組名)標記需要被校驗的數據,否則會不生效。
註意:
  檢測到數據異常後,系統會向外拋出異常,如果做了統一異常處理,可以根據 postman 測試的結果,找到控制台列印出的 相應的異常,並處理。

Step3:
  處理異常。使用 BindingResult 可以獲取到檢測結果,然後進行處理。
  也可以使用 全局統一異常 處理(@RestControllerAdvice 與 @ExceptionHandler),處理檢測結果。
註:
  統一異常處理參考:https://www.cnblogs.com/l-y-h/p/12781586.html#_label2

 

(2)使用:
Step1:
  在相關的 Bean 上標註註解,並寫上指定信息。

import lombok.Data;

import javax.validation.constraints.NotNull;
import java.io.Serializable;

@Data
public class Emp implements Serializable {
    private static final long serialVersionUID = 281903912367009575L;

    @NotNull(message = "id 不能為 null")
    private Integer id;

    @NotNull(message = "name 不能為 null")
    private String name;
    
    private Double salary;
    
    private Integer age;
    
    private String email;
}

 

 

Step2:
  修改 Controller 方法,使用 @Valid 註解標記需要檢測的數據。

@RestController
@RequestMapping("api")
public class EmpController {
    @Resource
    private EmpService empService;

    @PostMapping("/emp")
    public Result createEmp(@Valid @RequestBody Emp emp) {
        return Result.ok().data("items", emp).message("數據插入成功");
    }
}

 

 

Step3:
  使用 postman 測試一下。會拋出 MethodArgumentNotValidException 異常。

 

 

控制台列印的信息:

 

 

Step4:
  可以使用 BindingResult 去處理捕獲到的數據併進行相關處理。

@RestController
@RequestMapping("api")
public class EmpController {
    @Resource
    private EmpService empService;

    @PostMapping("/emp")
    public Result createEmp(@Valid @RequestBody Emp emp, BindingResult result) {
        if (result.hasErrors()) {
            Map<String, String> map = new HashMap<>();
            // 獲取校驗結果,遍歷獲取捕獲到的每個校驗結果
            result.getFieldErrors().forEach(item ->{
                // 獲取校驗的信息
                String message = item.getDefaultMessage();
                String field = item.getField();
                // 存儲得到的校驗結果
                map.put(field, message);
            });
            return Result.error().message("數據不合法").data("items", map);
        }
        return Result.ok().data("items", emp).message("數據插入成功");
    }
}

 

 

使用 Postman 測試。

 

 

Step5:
  通過上面的步驟,已經可以捕獲異常、處理異常,但是每次都是在業務方法中手動處理邏輯,這樣的實現,代碼肯定會冗餘。可以將其抽出,使用 統一異常處理,每次異常發生時,將其捕獲。
  根據 Step3 可以看到會拋出 MethodArgumentNotValidException 異常,所以需要將其捕獲。
  需要使用 @RestControllerAdvice 與 @ExceptionHandler。

@RestControllerAdvice
public class GlobalExceptionHandler {
    private Logger logger = LoggerFactory.getLogger(getClass());

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handlerValidException(MethodArgumentNotValidException e) {
        logger.error(e.getMessage(), e);
        BindingResult result = e.getBindingResult();
        Map<String, String> map = new HashMap<>();
        // 獲取校驗結果,遍歷獲取捕獲到的每個校驗結果
        result.getFieldErrors().forEach(item ->{
            // 存儲得到的校驗結果
            map.put(item.getField(), item.getDefaultMessage());
        });
        return Result.error().message("數據校驗不合法").data("items", map);
    }
}

 

 

相應的業務方法里,不需要再用 BindingResult 去處理數據了(即 Step2 的狀態)。

@RestController
@RequestMapping("api")
public class EmpController {
    @Resource
    private EmpService empService;

    @PostMapping("/emp")
    public Result createEmp(@Valid @RequestBody Emp emp) {
        return Result.ok().data("items", emp).message("數據插入成功");
    }
}

 

 

使用 Postman 測試。

 

 

4、JSR 303 分組校驗

(1)為什麼使用 分組校驗?
  通過上面的過程,可以瞭解到單個方法的校驗規則。
  如果出現多個方法,都需要校驗 Bean,且校驗規則不同的時候,怎麼辦呢?
  分組校驗就可以去解決該問題,每個分組指定不同的校驗規則,不同的方法執行不同的分組,就可以得到不同的校驗結果。

(2)基本認識
  JSR 303 的每個註解都預設具備三個屬性:
    message 用來定義數據校驗失敗後的提示消息,預設讀取配置文件的內容。
      全局搜索 ValidationMessages.properties,可以看到預設的信息。

    groups 用來定義分組,其是一個 class 數組,可以指定多個分組。

String message() default "{javax.validation.constraints.NotNull.message}";

Class<?>[] groups() default { };

Class<? extends Payload>[] payload() default { };

 

(3)使用分組步驟:
Step1:
  定義一個空介面,用於指定分組,內部不需要任何實現。

Step2:
  指定 註解時,通過 groups 指定分組。用於指定在某個分組條件下,才去執行校驗規則。

Step3:
  在相關的業務方法上,通過 @Validated 註解指定分組,去指定校驗。
註:
  使用分組校驗後,Bean 註解上若不指定分組,則不會執行校驗規則。

(4)使用:
Step1:
  創建分組介面。
  創建兩個分組介面 AddGroup、UpdateGroup。
其中:
  AddGroup 用於指定 添加數據 時的校驗規則(比如:id、name 均不為 null)。
  UpdateGroup 用於指定 修改數據 時的校驗規則(比如:name 不允許為 null)。

 

 

Step2:
  給 Bean 添加註解,並指定分組信息。

@Data
public class Emp implements Serializable {
    private static final long serialVersionUID = 281903912367009575L;

    @NotNull(message = "id 不能為 null", groups = {AddGroup.class})
    private Integer id;

    @NotNull(message = "name 不能為 null", groups = {AddGroup.class, UpdateGroup.class})
    private String name;

    private Double salary;

    private Integer age;

    private String email;
}

 

 

Step3:
  在業務方法上,通過 @Validated 註解指定分組,去指定校驗。
如下例,定義兩個方法,Post 請求會觸發 createEmp 方法,Put 請求會觸發 UpdateEmp 方法。

@RestController
@RequestMapping("api")
public class EmpController {
    @Resource
    private EmpService empService;

    @PostMapping("/emp")
    public Result createEmp(@Validated({AddGroup.class}) @RequestBody Emp emp) {
        return Result.ok().data("items", emp).message("數據插入成功");
    }

    @PutMapping("/emp")
    public Result UpdateEmp(@Validated({UpdateGroup.class}) @RequestBody Emp emp) {
        return Result.ok().data("items", emp).message("數據插入成功");
    }
}

 

 

Step4:
  使用 Postman 測試,發送 Post 請求,觸發 createEmp 方法,執行 AddGroup 校驗規則。
  檢測 id、name 是否合法。

 

 

發送 Put 請求,觸發 UpdateEmp 方法,執行 UpdateGroup 校驗規則。
只檢測 name 是否合法。

 

 

5、JSR 303 自定義校驗註解

(1)為什麼使用自定義校驗註解?
  上面的註解滿足不了業務需求時,可以自定義校驗註解,自定義校驗規則。

(2)步驟:
Step1:
  需要自定義一個校驗註解。
  可以創建一個 ValidationMessages.properties 用於保存預設的 message 信息。

Step2:
  需要自定義一個校驗器,即自定義校驗規則。
  實現 ConstraintValidator 介面,並重寫相關方法。
註:
  initialize 方法用於初始化,可以獲取 自定義的屬性的值。
  isValid 方法用於校驗,可以獲取到實際的值,然後與自定義的屬性值進行比較。

Step3:
  將校驗註解 與 校驗器 關聯起來。
  @Constraint(validatedBy = {TestValidConstraintValidator.class})

(3)使用:
  如下例,自定義一個校驗規則,判斷數據長度是否合法。
  預設為 String 屬性,當 String 為 Null 或者 長度大於 5 時,校驗不通過。
  可以自定義 長度。
Step1:
  自定義一個校驗註解,@TestValid,用於判斷一個值的長度是否合法。

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 用於判斷一個值的長度是否合法
 */
@Target({FIELD})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {TestValidConstraintValidator.class})
public @interface TestValid {
    String message() default "{com.lyh.test.TestValid.message}";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };

    /**
     * 返回一個長度
     * @return 預設為 5
     */
    int length() default 5;
}

 

 

配置文件內容:

 

 

Step2:
  自定義一個校驗器TestValidConstraintValidator, 用於檢測值是否合法。

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

/**
 * 實現 ConstraintValidator 介面,
 * 其中 ConstraintValidator 的泛型,一個需要指定自定義的註解,一個需要指定需要獲取的值的類型。
 * 比如:
 *  ConstraintValidator<TestValid, String> 中
 *      TestValid   表示自定義註解
 *      String      表示獲取的值的類型
 * 即定義規則,判斷一個 String 的值的長度是否滿足條件
 */
public class TestValidConstraintValidator implements ConstraintValidator<TestValid, String> {

    /**
     * 用於保存自定義的(預設)長度
     */
    private int length;

    /**
     * 初始化方法,獲取預設數據
     * @param constraintAnnotation 註解對象
     */
    @Override
    public void initialize(TestValid constraintAnnotation) {
        length = constraintAnnotation.length();
    }

    /**
     * 自定義校驗規則,如果 String 為 Null 或者 長度大於 5,則校驗失敗(返回 false)
     * @param value 需要校驗的值
     * @param context
     * @return true 表示校驗成功,false 表示校驗失敗
     */
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value == null ? false : length > value.length();
    }

}

 

 

Step3:
  使用註解。

@Data
public class Emp implements Serializable {
    private static final long serialVersionUID = 281903912367009575L;

    @NotNull(message = "id 不能為 null", groups = {AddGroup.class})
    private Integer id;

    @TestValid(groups = {AddGroup.class})
    @NotNull(message = "name 不能為 null", groups = {AddGroup.class, UpdateGroup.class})
    private String name;

    private Double salary;

    private Integer age;

    @TestValid(length = 10, message = "值不能為 Null 且長度不超過 10", groups = {AddGroup.class})
    private String email;
}

 

 

使用 Postman 測試。
  name、email 都不存在時,會被捕獲數據異常。

 

 

name 數據不合法、email 數據合法時,name 會被捕獲。

 

 

三、JSR 303 相關註解

1、空檢查相關註解

註解                      註解詳情
@Null                  被指定的註解元素必須為 Null
@NotNull               任意類型,不能為 Null,但可以為空,比如空數組、空字元串。
@NotBlank              針對字元串,不能為 Null,且去除前後空格後的字元串長度要大於 0。
@NotEmpty              針對字元串、集合、數組,不能為 Null,且長度要大於 0。

 

 

 

 

 

 

 

 

2、長度檢查

註解                      註解詳情
@Size                   針對字元串、集合、數組,判斷長度是否在給定範圍內。
@Length                 針對字元串,判斷長度是否在給定範圍內。

 

 

 

 

3、布爾值檢查

註解                      註解詳情
@AssertTrue             針對布爾值,用來判斷布爾值是否為 true
@AssertFalse            針對布爾值,用來判斷布爾值是否為 false

 

 

 

 

4、日期檢查

註解                      註解詳情
@Past                  針對日期,用來判斷當前日期是否為 過去的日期
@Future                針對日期,用來判斷當前日期是否為 未來的日期

 

5、數值檢查

註解                      註解詳情
@Max(value)         針對字元串、數值,用來判斷是否小於等於某個指定值
@Min(value)         針對字元串、數值,用來判斷是否大於等於某個指定值

 

 

 

 

6、其他

註解                      註解詳情
@Pattern             驗證字元串是否滿足正則表達式
@Email               驗證字元串是否滿足郵件格式
@Url                 驗證是否滿足 url 格式

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Pet 7 { 8 private: 9 string name; 10 int age; 11 string color; 12 public: ...
  • 在我剛接觸編程的時候, 那時候面試小題目很喜歡問下麵這幾類問題 1' 浮點數如何和零比較大小? 2' 浮點數如何轉為整型? 然後過了七八年後這類問題應該很少出現在面試中了吧. 剛好最近我遇到線上 bug, 同大家交流科普下 問題最小現場 #include <stdio.h> int main(voi ...
  • 1 #include <iostream> 2 3 using namespace std; 4 5 class Pet 6 { 7 public: 8 virtual void Speak(){cout<<"How does a pet speak?"<<endl;} 9 }; 10 11 cla ...
  • 集群相關 查看k8s版本 kubectl version 查看api版本 kubectl api-versions 查看集群信息 kubectl cluster-info 查看集群健康情況 kubectl get cs 查看事件 kubectl get events Node節點 查看節點列表信息 ...
  • 一丶簡介 Fanout Exchange 不處理路由鍵。你只需要簡單的將隊列綁定到交換機上。一個發送到交換機的消息都會被轉發到與該交換機綁定的所有隊列上。很像子網廣播,每檯子網內的主機都獲得了一份複製的消息。Fanout交換機轉發消息是最快的。 業務場景: 1.訂單服務需要同時向簡訊服務和push服 ...
  • 1. spring boot lll starter自動化框架介紹 1.1. 前言 舔著臉來介紹一波我剛寫的自動化框架, "spring boot lll starter" 框架是經由我企業實戰總結的一套,適用於項目起始構建的框架,適配了管理後臺和微服務項目兩種方案的代碼生成 我做了一個簡短的dem ...
  • C語言被忽視的一些小東西!C語言基礎教程之錯誤處理。 C 語言不提供對錯誤處理的直接支持,但是作為一種系統編程語言,它以返回值的形式允許您訪問底層數據。在發生錯誤時,大多數的 C 或 UNIX 函數調用返回 1 或 NULL,同時會設置一個錯誤代碼errno,該錯誤代碼是全局變數,表示在函數調用期間 ...
  • 任何可以產生對象的方法或者類,都可以稱之為工廠。單例就是所謂的靜態工廠。 為什麼jdk中有了new,還需要工廠呢? a、靈活的控制生產過程 b、給對象加修飾、或者給對象加訪問許可權,或者能夠在對象生產過程中添加一些日誌信息,再或者根據應用場景添加一些實際的業務處理等等。 1、靜態工廠 單例模式:一種特 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...