1. 程式人生 > >學習Spring Boot:(十)使用hibernate validation完成資料後端校驗

學習Spring Boot:(十)使用hibernate validation完成資料後端校驗

前言

後臺資料的校驗也是開發中比較注重的一點,用來校驗資料的正確性,以免一些非法的資料破壞系統,或者進入資料庫,造成資料汙染,由於資料檢驗可能應用到很多層面,所以系統對資料校驗要求比較嚴格且追求可變性及效率。

瞭解

瞭解一點概念性的東東。
* JSR 303 是 Java 為 Bean 資料合法性校驗提供的標準框架,它已經包含在 JavaEE 6.0 中 。
* Hibernate Validator 是 JSR 303 的一個參考實現,所以它多實現了幾個校驗規則。
* Spring 4.0 擁有自己獨立的資料校驗框架,同時支援 JSR303 標準的校驗框架。
* 在已經標註了 JSR303 註解的表單/命令物件前標註一個@Valid,Spring MVC 框架在將請求引數繫結到該入參物件後,就會呼叫校驗框架根據註解宣告的校驗規則實施校驗
* Spring MVC 是通過對處理方法簽名的規約來儲存校驗結果的:前一個表單/命令物件的校驗結果儲存到隨後的入參中,這個儲存校驗結果的入參必須是 BindingResult 或Errors 型別,這兩個類都位於org.springframework.validation 包中。
* 需校驗的 Bean 物件和其繫結結果物件或錯誤物件時成對出現的,它們之間不允許宣告其他的入參
* Errors 介面提供了獲取錯誤資訊的方法,如 getErrorCount() 或getFieldErrors(String field)
* BindingResult 擴充套件了 Errors 介面。

支援的註解

JSR 提供的校驗註解:

@Null   被的註解元素必須為 null    
@NotNull    被註解的元素必須不為 null    
@AssertTrue     被註解的元素必須為 true    
@AssertFalse    被註解的元素必須為 false    
@Min(value)     被註解的元素必須是一個數字,其值必須大於等於指定的最小值    
@Max(value)     被註解的元素必須是一個數字,其值必須小於等於指定的最大值    
@DecimalMin(value)  被註解的元素必須是一個數字,其值必須大於等於指定的最小值    
@DecimalMax
(value) 被註解的元素必須是一個數字,其值必須小於等於指定的最大值 @Size(max=, min=) 被註解的元素的大小必須在指定的範圍內 集合或陣列 集合或陣列的大小是否在指定範圍內 @Digits (integer, fraction) 被註解的元素必須是一個數字,驗證是否是符合指定格式的數字,interger指定整數精度,fraction指定小數精度。 @Past 被註解的元素必須是一個過去的日期 @Future 被註解的元素必須是一個將來的日期 @Pattern(regex=,flag=) 被註解的元素必須符合指定的正則表示式

Hibernate Validator 提供的校驗註解:

@NotBlank(message =)   驗證字串非null,且長度必須大於0    
@Email  被註釋的元素必須是電子郵箱地址    
@Length(min=,max=)  被註解的值大小必須在指定的範圍內    
@NotEmpty   被註解的字串的必須非空    
@Range(min=,max=,message=)  驗證該值必須在合適的範圍內

可以在需要驗證的屬性上,使用多個驗證方式,它們同時生效。
spring boot web 已經有 hibernate-validation 的依賴,所以不需要再手動新增依賴。

使用

首先我在我的實體類上寫了幾個校驗註解。

public class SysUserEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    //主鍵
    private Long id;
    //使用者名稱
    @NotBlank(message = "使用者名稱不能為空", groups = {AddGroup.class, UpdateGroup.class})
    private String username;
    //密碼
    @NotBlank(message = "密碼不能為空", groups = {AddGroup.class})
    private String password;
    //手機號
    @Pattern(regexp = "^1([345789])\\d{9}$",message = "手機號碼格式錯誤")
    @NotBlank(message = "手機號碼不能為空")
    private String mobile;
    //郵箱
    @Email(message = "郵箱格式不正確")
    private String email;
    //建立者
    private Long createUserId;
    //建立時間
    private Date createDate;
// ignore set and get

使用@Validated進行校驗

首先了解下:
關於@Valid和@Validated的區別聯絡
* @Valid: javax.validation, 是javax,也是就是jsr303中定義的規範註解
* @Validated: org.springframework.validation.annotation, 是spring自己封裝的註解。引數校驗失敗丟擲 org.springframework.validation.BindException 異常。

@Validated@Valid 的一個變種,擴充套件了 @Valid 的功能,支援 group分組校驗 的寫法,所以為了校驗統一,儘量使用 @Validated

在controller自定義一個介面

@PostMapping("/valid")
    public ResponseEntity<String> valid(@Validated @RequestBody SysUserEntity user, BindingResult result) {
        if (result.hasErrors()) {
            return ResponseEntity.status(BAD_REQUEST).body("校驗失敗");
        }
        return ResponseEntity.status(OK).body("校驗成功");
    }

需要注意的有幾點:
* 需要校驗物件的時候,需要加上 spring 的校驗註解 @Validated ,表示我們需要 spring 對它進行校驗,而校驗的資訊會存放到其後的BindingResult中。
* BindingResult 必須和檢驗物件緊鄰,中間不能穿插任何引數,如果有多個校驗物件 @Validated @RequestBody SysUserEntity user, BindingResult result, @Validated @RequestBody SysUserEntity user1, BindingResult result1

我在前端用 Swagger 進行測試下。
我傳送一個 body,將 手機號輸錯:

{
  "createDate": "",
  "createUserId": 0,
  "email": "[email protected]",
  "id": 0,
  "mobile": "12354354",
  "password": "123",
  "username": "12312"
}

後端除錯下 BindingResult 的結果,發現結果:
image
只要注意下 errors 屬性,它是校驗所有不符合規則的,是一個數組。

分組校驗

有時候 ,我們在新增和更新的時候校驗效果是不一樣的。例如上面,我在User新增的時候需要判斷密碼是不是為空,但是更新的時候我不做校驗。這個時候就也要用到分組校驗了。

@NotBlank(message = "密碼不能為空", groups = {AddGroup.class})
private String password;

將Contoller中的校驗修改下。

(@Validated({AddGroup.class}) @RequestBody SysUserEntity user, BindingResult result)

上面的意思是隻有分組是AddGroup的校驗才生效,其餘的校驗忽略。

經過我測試,把分組情況分下:
1. 在controller校驗沒加分組的時候,只對實體類的沒有分組的註解有效。
2. 在controller校驗加分組的時候,只對實體類的當前分組的註解有效,沒有註解的也無效。
3. 當校驗有兩個分組的時候@Validated({AddGroup.class, UpdateGroup.class}),滿足當前兩個分組其中任意一個都可以校驗,兩個註解同時一起出現,也沒問題,而且檢驗不通過的資訊不會重複。

自定義校驗

有時候系統提供給我們的校驗註解,並不夠用,我們可以自定義校驗,來滿足我們的業務需求。

例如:現在我們有一個需求,需要檢測一條資訊的敏感詞彙,如sb ……文明人,舉個栗子 ……

自定義校驗註解
// 註解可以用在哪些地方
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
// 指定校驗規則實現類
@Constraint(validatedBy = {NotHaveSBValidator.class})
public @interface NotHaveSB {
    //預設錯誤訊息
    String message() default "不能包含字元sb";
    //分組
    Class<?>[] groups() default {};
    //負載
    Class<? extends Payload>[] payload() default {};
    //指定多個時使用
    @Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
    @Retention(RUNTIME)
    @Documented
    @interface List {
        NotHaveSB[] value();
    }

}
規則校驗實現類
// 可以指定檢驗型別,這裡選擇的是 String
public class NotHaveSBValidator implements ConstraintValidator<NotHaveSB, String> {
    @Override
    public void initialize(NotHaveSB notHaveSB) {

    }

    /**
     *
     * @param s 待檢驗物件
     * @param constraintValidatorContext 檢驗上下文,可以設定檢驗的錯誤資訊
     * @return false 代表檢驗失敗
     */
    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        return !StringUtils.isNotBlank(s) || !s.toLowerCase().contains("sb");
    }
}

所有的驗證者都需要實現ConstraintValidator介面,它的介面也很形象,包含一個初始化事件方法,和一個判斷是否合法的方法。

測試一下喂

現在我的使用者類上,也沒什麼多餘的欄位拿出來測試,暫時把 password 欄位拿來測試吧。

//@NotBlank(message = "密碼不能為空", groups = AddGroup.class)
    @NotHaveSB
    private String password;

image

手動校驗

這個是我最終想要的處理方式。
由於現在都是前後端分離開發的,校驗失敗的時候,丟擲自定義的異常,然後統一處理這些異常,最後將相關的錯誤提示資訊返回給前端處理。

新建一個驗證工具類

public class ValidatorUtils {
    private static Validator validator;

    static {
        validator = Validation.buildDefaultValidatorFactory().getValidator();
    }

    /**
     * 手動校驗物件
     *
     * @param object 待校驗物件
     * @param groups 待校驗的組
     * @throws KCException 校驗不通過,則丟擲 KCException 異常
     */
    public static void validateEntity(Object object, Class<?>... groups)
            throws KCException {
        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
        if (!constraintViolations.isEmpty()) {
            String msg = constraintViolations.parallelStream()
                    .map(ConstraintViolation::getMessage)
                    .collect(Collectors.joining(","));
            throw new KCException(msg);
        }
    }
}

它主要做的事情就是驗證我們的待驗證物件,驗證不同通過的時候,丟擲自定義異常,在後臺統一處理異常就可以了。

在業務中直接呼叫就可以了,有分組新增分組就行

@PostMapping("/valid1")
    public ResponseEntity<String> customValid(@RequestBody SysUserEntity user) {
        ValidatorUtils.validateEntity(user);
        return ResponseEntity.status(OK).body("校驗成功");
    }

最後測試一下,檢視返回結果是否符合預期:

image

手動校驗的補充

決定還是採用註解的形式進行編碼,本來想用處理方法引數的裝配進行檢驗,寫好了發現和 @responseBody 不能同時使用,然後發現還是可以使用 @Validated 直接校驗,丟擲異常, 進行捕捉異常統一處理。

    @PostMapping()
    @ApiOperation("新增")
    public ResponseEntity insert(@Validated SysUserAddForm user) 

在全域性異常處理裡面加上 處理繫結引數異常 org.springframework.validation.BindException

    /**
     * 引數檢驗違反約束(資料校驗)
     * @param e BindException
     * @return error message
     */
    @org.springframework.web.bind.annotation.ExceptionHandler(BindException.class)
    public ResponseEntity<String> handleConstraintViolationException(BindException e) {
        LOGGER.debug(e.getMessage(), e);
        return ResponseEntity.status(BAD_REQUEST).body(
                e.getBindingResult()
                        .getAllErrors()
                        .stream()
                        .map(DefaultMessageSourceResolvable::getDefaultMessage)
                        .collect(Collectors.joining(",")));
    }

相關推薦

學習Spring Boot使用hibernate validation完成資料

前言 後臺資料的校驗也是開發中比較注重的一點,用來校驗資料的正確性,以免一些非法的資料破壞系統,或者進入資料庫,造成資料汙染,由於資料檢驗可能應用到很多層面,所以系統對資料校驗要求比較嚴格且追求可變性及效率。 瞭解 瞭解一點概念性的東東。 * JSR 3

使用spring validation完成資料

前言 資料的校驗是互動式網站一個不可或缺的功能,前端的js校驗可以涵蓋大部分的校驗職責,如使用者名稱唯一性,生日格式,郵箱格式校驗等等常用的校驗。但是為了避免使用者繞過瀏覽器,使用http工具直接向後端請求一些違法資料,服務端的資料校驗也是必要的,可以防止髒資

Spring Boot開發常用的熱部署方式彙總

前言平時開發Sprig Boot的時候,經常改動個小小的地方就要重新啟動專案,這無疑是一種很差的體驗。在此,博主收集了3種熱部署的方案供大家選擇。本文為轉載內容:Spring Boot乾貨系列:(十)開發常用的熱部署方式彙總正文目前博主用過的有三種:Spring Loaded

學習 Spring BootSpring Boot Junit 單元測試

前言 JUnit 是一個迴歸測試框架,被開發者用於實施對應用程式的單元測試,加快程式編制速度,同時提高編碼的質量。 JUnit 測試框架具有以下重要特性: 測試工具 測試套件 測試執行器 測試分類 瞭解 Junit 基礎方法 加入依賴 在 p

Spring BootSpring Boot使用單元測試

前言這次來介紹下Spring Boot中對單元測試的整合使用,本篇會通過以下4點來介紹,基本滿足日常需求Service層單元測試Controller層單元測試新斷言assertThat使用單元測試的回滾正文Spring Boot中引入單元測試很簡單,依賴如下:1 2 3 4

Spring Boot 入門整合Redis哨兵模式,實現Mybatis二級快取

本片文章續《Spring Boot 入門(九):整合Quartz定時任務》。本文主要基於redis實現了mybatis二級快取。較redis快取,mybaits自帶快取存在缺點(自行谷歌)。本文是基於docker安裝redis主從模式。 1.redis安裝 (1)首先安裝redis叢集模式,建立redis目錄

Spring Boot預設日誌logback配置解析

前言今天來介紹下Spring Boot如何配置日誌logback,我們帶著下面幾個問題來學習。如何引入日誌?日誌輸出格式以及輸出方式如何配置?程式碼中如何使用?正文Spring Boot在所有內部日誌中使用Commons Logging,但是預設配置也提供了對常用日誌的支援,

Spring Boot啟動原理解析

前言前面幾章我們見識了SpringBoot為我們做的自動配置,確實方便快捷,但是對於新手來說,如果不大懂SpringBoot內部啟動原理,以後難免會吃虧。所以這次博主就跟你們一起一步步揭開SpringBoot的神祕面紗,讓它不在神祕。正文我們開發任何一個Spring Boot

spring-boot-route多資料來源切換

前面我們已經介紹了三種方式來操作資料庫,在實際開發中,往往會出現一個服務連線多個數據庫的需求,這時候就需要在專案中進行靈活切換資料來源來完成多個數據庫操作。這一章中,我們使用jdbcTemplate來學習多資料來源的配置。 ## 一 準備工作 ### 1.1 建庫、建表 我們新建兩個庫`db1`和`db

深度學習之路Keras中mnist資料集測試

Keras環境搭建 本地環境 MacOS 一、安裝Anaconda 1、下載Anaconda最新版本:官網下載地址 附:清華映象源 2、下載後直接安裝,點選next 3、檢測版本 開啟終端 輸入conda -

Seafile深入學習 對接Amazon S3儲存

客戶如果問,怎麼和Amazon S3對接,你怎麼回答。首先,你需要安裝Seafile伺服器,然後購買S3服務。接下來在Seafile中修改seafile.conf配置檔案來為Seafile的三種資料物件commit,fs,block建立三個桶。配置檔案中,為桶起名字,key_

Spring Boot學習之旅springboot 整合 fastjson

springboot 預設使用的 jackson 但是聽說某寶的fastjson 效能很好,而且平時用的習慣,所以來整合一下。 首先在pom 中匯入依賴 <dependency> <groupId>

Spring Boot 入門整合 WebSocket, 實時顯示系統日誌

以前面的部落格為基礎,最近一篇為Spring Boot 入門(十):整合Redis哨兵模式,實現Mybatis二級快取。本篇部落格主要介紹了Spring Boot整合 Web Socket進行日誌的推送,並實時顯示在頁面上。 1.匯入jar包 第一個jar包是websocket的,第二個jar包是關於環形佇列

Spring Boot 入門報表匯出,對比poi、jxl和esayExcel的效率

本片部落格是緊接著Spring Boot 入門(十一):整合 WebSocket, 實時顯示系統日誌寫的 關於poi、jxl和esayExcel的介紹自行百度。 jxl最多支援03版excel,所以單個sheet頁面最多隻能匯出65536條資料。 我直接將excel匯入到瀏覽器並開啟,以下統計匯出時長指將資

Spring Boot教程整合elk1

分享圖片 operation you 運行 hot href iba for 分享 elk 簡介 Elasticsearch是個開源分布式搜索引擎,它的特點有:分布式,零配置,自動發現,索引自動分片,索引副本機制,restful風格接口,多數據源,自動搜索負載等。

企業級 Spring Boot 教程 上傳文件

throw www. form mapping frame 自己的 rop map 分布 這篇文章主要介紹,如何在springboot工程作為服務器,去接收通過http 上傳的multi-file的文件。 構建工程 為例創建一個springmvc工程你需要spring-bo

Spring boot入門快速搭建Spring boot專案

(一)Spring boot介紹   本部分摘自:https://www.zhihu.com/question/64671972/answer/223383505   Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發過程。該框架使用了特定

Spring boot入門SpringBoot整合結合AdminLTE(Freemarker),利用generate自動生成程式碼,利用DataTable和PageHelper進行分頁顯示

  關於SpringBoot和PageHelper,前篇部落格已經介紹過Spring boot入門(二):Spring boot整合MySql,Mybatis和PageHelper外掛,前篇部落格大致講述了SpringBoot如何整合Mybatis和Pagehelper,但是沒有做出實際的範例,本篇部落格是連

Spring Boot 入門 自動化配置實現

自動化配置實現 我們在上章編寫入門案例的時候,我們使用的是Spring Mvc 作為我們的表現層框架,但是我們都知道我們要使用Spring Mvc 我們就需要在web.xml檔案中配置Spring Mvc 的前端控制器DispatcherServlet。但是我們

Spring Boot總結構建高效能的服務平臺

使用Spring Cloud開發的微服務,其獨立而有相對隔離的特性,與Docker的理念有異曲同工之妙,所以使用Docker來發布微服務,能夠發揮其更大的優勢,並且可以非常輕易的構建一個高效能和高可用的服務平臺; Docker