1. 程式人生 > >Spring MVC利用Hibernate Validator實現後端資料校驗

Spring MVC利用Hibernate Validator實現後端資料校驗

        吐槽一下,網上坑好多啊!不過採坑才能學習,寫bug能力-1。

JSR 303、JSR 349與Bean Validator

        籠統來說,就是Java規定了一套關於驗證器的API,規範先後發了兩版,就是所謂的JSR 303與JSR 349。然後提出了基於規範的實現:Bean Validator。JSR 303是最早的,其對應了Bean Validator 1.0的版本,比較菜,然後後來擴充套件了JSR 349,提出了依賴注入、註解等內容,稱為了Bean Validator。

        關於Bean Validator,這並不是一項技術,也算是一種規範,需要對其實現。而博文使用的Hibernate Validator就是其中一種。因為Spring MVC本身不提供validator的,所以我們需要其他的代替。下面是Wikipedia的一些內容:

Java Bean Validation (JSR 303) originated as a framework that was approved by the JCP as of 16 November 2009 and accepted as part of the Java EE 6 specification. The Hibernate team provides with Hibernate Validator the reference implementation

 of Bean Validation and also created the Bean Validation TCK any implementation of JSR 303 needs to pass.

大概翻譯一下,就是Java Bean的資料驗證(JSR 303),起源於2009今年批准的框架,併成為Java EE 6的一部分。而hibernate團隊提供了其一種實現。所以Hibernate Validator其實與SSH的那個H:Hibernate,並不是其子集。(可能吧,並沒有接觸過hibernate)。

Hibernate Validator

        上面也說過了其由來,這裡簡單說明一下執行過程。JSR 349提出了註解約束,所以一般使用註解了。除去在專案中的配置,我們可以在Java Bean中的屬性上使用註解來進行資料約束。在Spring MVC中,利用IoC處理物件的時候就會觸發資料驗證。例如提交表單的時候,dispatcher接收的時候,就會根據約束來判定資料是否正確。如果有不正確的,就會根據宣告的message,將其新增到Error中,Spring MVC中一般使用BlindingResult物件來獲取,然後就能對錯誤的資料進行處理了。

        整體上,除了配置以外,還是比較簡單的。

必須的一些jar包

jar包是當時看的某本書,裡面原始碼找的,可能會比較老,找新版本也可以。然後這裡有一點是比較坑的,網上大部分說的是2個包或者3個包——除了classmate的包。然後我這邊是用前3個包的話報錯了,很坑,然後看log大概是這樣的錯誤:

java.lang.NoClassDefFoundError: com/fasterxml/classmate/Filter

其實這裡當時沒截圖,錯誤很多,但是比較重要的就是這個,發現是少這個包了。上網搜了一下,找了這個0.8的包,貌似最新的是1.0(不過好長時間不更了)。然後在idea的話,記得在Artifacts裡面新增,否則直接加jar包並沒有什麼用。這裡我提供一下下載吧,百度雲盤,掛了可以評論(大概能補)

連結:https://pan.baidu.com/s/1tsDsoHmM8xjA0MGy3CzWTQ 
提取碼:qea7 

註解約束Bean

        JSR 349新增的,還是比較方便快捷的,先提供一個demo感受一下:

public class User {
    @Size(min = 6,max = 16,message = "{user.account.size}")
    @NotNull(message = "{user.account.notNull}")
    private String account;
    @Size(min = 6,max = 16,message = "{user.passwd.size}")
    @NotNull(message = "{user.passwd.notNull}")
    private String passwd;
    @Size(min = 11,max = 11,message = "{user.phone.size}")
    @NotNull(message = "{user.phone.notNull}")
    private String phone;
    @Email(message = "{user.email.email}")
    private String email;
    @Max(value = 10,message = "{user.userName.max}")
    private String userName;
}

在屬性上新增不同的註解,代表不同的約束,後面的message屬性就表示瞭如果違反約束,應該報怎樣的錯誤。message後面會詳談。

        關於這些註解,就直接放找到的了。

Bean Validation內建註解

註解名 簡單介紹 例子
@AssertFalse boolean型別必須為false  
@AssertTrue  boolean型別必須為true  
@Max 小於指定數 @Max(value = 150)
@Min 大於指定數  
@DecimalMax  小於指定數 @DecimalMax("2.2")
@DecimalMin 大於指定小數 @DecimalMin("0.01")
@Digits 指定整數位小數位的最大長度 @Digits(integer = 5,fraction = 2)
@Future 必須是未來的一個日期  
@Past 必須為過去的日期  
@NotNull 不為null  
@Null 必須為null  
@Pattern 值必須匹配正則表示式 @Pattern(regext = "\\d{3}")
@Size 值長度必須在規定的之間 @Size(min = 2,max = 100)

其實也沒啥好說的,注意下@DecimalMax註解和@Max的區別吧。來自Stack Overflow,簡單的區別,@DecimalMax接收的是一個字串,所以支援例如大數類的匹配,而@Max則不能。其他情況例如double等型別,實際上兩者是都能完成的。

Hibernate Validation擴充套件註解

註解名 簡單介紹 例子

@Email

必須是e-mail地址  
@Length 字串大小必須在指定的範圍內 @Length(min=2,max=10)
@NotEmpty 字串的必須非空  
@Range 元素必須在合適的範圍內 @Range(min=1,max=100)
@NotBlank 字串的必須非空  
@CreditCardNumber 字串必須通過Luhn校驗演算法(例如銀行卡)  

其實有些解釋也是奇奇怪怪的,不過用得不太多= =有機會再找找具體用法和解釋吧。

message資訊

        一般我們是利用註解標誌屬性,那麼當屬性出問題的時候我們需要返回錯誤資訊,而這些不同的錯誤匹配的錯誤資訊需要自己設定,不管怎樣都要在註解上寫message屬性,message屬性裡面就寫返回的錯誤資訊,這些會被BindingResult物件給獲取。其使用可以參考最上面的demo,或者看這裡:

public class User{
    @Email(message = "email error")
    String email1;
    @Email(message = "{user.email.error}")
    String email2;
}

這裡用了兩種寫法,第一種,直接是一個String,第二種是利用properties檔案的情況,下面會詳細談。

properties檔案

這裡有一點錯,再研究一下

        我們可以使用properties檔案,將這些錯誤資訊作為對映給表達出來。關於properties檔案,可能有同學學Java SE的時候就碰到過了,但是這裡還是比較方便的,只用寫properties檔案就可以了,不像SE那樣還得用反射獲取資料來源啥的。下面提供一下簡單入門,想詳細瞭解的再搜搜相關內容吧。

        properties檔案,大概可以理解為像XML那樣的配置檔案。但是比較方便,就像Map一樣是用key、value的鍵值對完成了對映關係。字尾名為.properties,內容及其簡單,寫就完事了,例如:

NAME=iwts
AGE=18
GIRLFRIENDS=NONE

這樣就完成了一系列對映,寫法是按照Java SE的,key大寫了。注意等號前後不要有空格。放的位置,一般在專案中是在src資料夾下。這樣在使用相對路徑找的時候,直接就是“/”。在src/properties下也行。在Java SE中怎麼使用就不多說了。

        在Hibernate Validator中使用的話,內容還是這樣寫,不過key是指具體的錯誤,value則是錯誤資訊。key的命名一般按照原則:物件名.屬性名.錯誤型別。這樣也會比較好找,例如最上面demo的對應properties檔案:

user.account.size=使用者名稱長度錯誤,請保持6-16位之間
user.account.notNull=賬號不能為空
user.passwd.size=密碼長度錯誤,請保持在6-16位之間
user.passwd.notNull=密碼不能為空
user.phone.size=請輸入正確的手機號
user.phone.notNull=請輸入手機號
user.email.email=請輸入正確的郵箱
user.userName.max=名字長度不能超過10位

然後扔進src資料夾下就行了,有地方放就ok。

是否使用properties檔案?

        博主也沒啥經驗,就不帶節奏了,說一下自己的理解:快速開發或者寫小專案(大學狗標準大X期末專案實訓之類的),可以不要properties檔案,直接寫就完事了,修改也比較好找。但是對於大一點的話這樣寫比較規範,各種修改都可以,反正對資料來源是沒有影響的。

dispatcher上配置validator

        就是在dispatcher對應的XML配置檔案上配置validator,看網上有人也說是Spring MVC的配置檔案,博主還是習慣說dispatcher了。

        首先,不管怎樣一定要有的是這些::

<mvc:annotation-driven validator="validator"/>

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
        <property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property>
</bean>

註冊了validator的驅動,注意上面是將下面的validator的bean給註冊進去的。所以<mvc:annotation-driven>標籤的validator屬性要跟下面bean的id相對應。

        接下來就有點不同了。上面在談message的時候,可以選擇是否使用properties檔案。這裡根據是否選用配置是不一樣的。或者說,如果不使用properties檔案的話,上面就ok了,下面的都不用看了。如果使用properties檔案的話,總得宣告對映關係對吧,怎麼對映到這個資原始檔。所以需要有下面的配置檔案:

<!-- 驅動 -->
<mvc:annotation-driven validator="validator"/>

<!-- validator基本配置 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property>
    <!-- 對映資原始檔 -->
    <property name="validationMessageSource" ref="validationMessageSource"></property>
</bean>

<!-- properties檔案 -->
<bean id="validationMessageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <value>properties.user</value>
        </list>
    </property>
    <property name="fileEncodings" value="utf8"></property>
    <property name="cacheSeconds" value="120"></property>
</bean>

可以看到,主要是validator的bean裡面多了一個屬性,其ref指向了properties檔案的bean。所以ref屬性要與properties的bean的id一致。properties的bean裡面的list裡面value聲明瞭其位置。只能用相對路徑了,所以說properties檔案應該放在src下面。

如何使用

        其實上面配置完成基本就完事了,使用上,bean裡面當然要寫註解,剩下的在controller裡面的程式碼需要一些變化,例如這個demo裡面:

@RequestMapping("register.action")
public ModelAndView register(@Valid @ModelAttribute User user, BindingResult bindingResult, Model model){
    if(bindingResult.hasErrors()){
        ArrayList<String> errorList = new ArrayList<>();
        System.out.println("have error");
        return new ModelAndView("register_error-test");
    }
    System.out.println("ok");
    return new ModelAndView("register_success-test");
}

主要的是形參列表,可以看到對於user物件而言,多了一個@Valid註解。我們需要這個註解來宣告,依賴注入的時候順便來一波資料校驗。而後面的BindingResult物件,在資料校驗中,就是收集message,下面程式碼可以進行判定。也可以使用List進行儲存,例如新增到model裡面返回給檢視啊之類的。總之剩下的部分就看怎麼玩了。

一個測試

        其實看懂了就可以自己試一試,檢視的話隨便寫一個jsp或者html有個form標籤就行了。controller的話如果搞不明白dispatcher(至少知道怎麼用)的話還是推薦先搞明白controller之類的東西。上面的demo實際上就是一個小測試的合集了,可以複製程式碼試試看。不過自己寫一個bean會更方便吧。

Hibernate Validator分組校驗

        留坑,之前看到的,還沒仔細看。