play框架使用起來(14)-高級指南

高級指南
1、HTTP數據驗證

 數據驗證是應用程序健壯性的體現,在實際項目中也是必不可少的環節。Play內置了驗證器(Validation)的支持,並提供了非常靈活的使用方法。在Play項目中可以很簡單地對數據,模型對象(可能需要持久化)以及HTTP表單進行驗證。


1.1 使用Play驗證器#

      出於數據驗證的考慮,框架爲每個請求綁定了驗證器。應用代碼中可以通過以下三種方式對數據進行驗證:

      1.在控制器的Action方法中,直接使用驗證器對數據進行相應的驗證:

public static void hello(String name) {
     validation
.required(name);  //驗證name參數是否被賦值
     
...
}


      2.在控制器的Action方法的參數中,使用註解進行數據驗證:

public static void hello(@Required String name){
   
...
}


      3.定義域模型時直接爲屬性添加驗證註解,Action中就可以通過@Valid註解對該POJO參數進行驗證了:

public class User extends Model {
   
@Required
   
public String name;  //爲User的name屬性添加@Required驗證
   
...
}
//直接使用@Valid註解對POJO參數進行驗證
public static void save(@Valid User user) {
   
...
}


注意:

框架爲每個請求綁定了一個驗證器,同個驗證器可以校驗多個數據。


      當驗證不通過時,驗證器會將錯誤以play.data.validation.Error的形式保存,因此每個驗證器以集合的形式維護了一系列的error對象。每個error對象有key和message兩個屬性:

  • key:該屬性幫助我們標識引起錯誤的元素。key的值可以任意設定,默認與驗證的數據同名。
  • message:驗證消息,用於描述驗證不通過的錯誤信息。message可以是純文本或指向消息包(message bundle)的key(通常爲了支持國際化)。


      文字的描述可能有些空洞,接下來讓我們看看如何使用驗證器校驗簡單的HTTP參數:

public static void hello(String name) {
     validation
.required(name);
     
...
}

      以上這段程序代碼檢查了name參數是否被賦值。如果沒有,相應的錯誤就會被添加到當前的error集合中。如果有多個數據需要驗證,可以重複該操作:

public static void hello(String name, Integer age) {
     validation
.required(name);     //驗證name是否被賦值
     validation
.required(age);      //驗證age是否被賦值
     validation
.min(age, 0);        //驗證age不小於0
     
...
}

1.2 驗證消息(error對象中的message屬性)#

      在驗證結束後我們可以檢查是否有error產生,並將驗證消息打印出來:

public static void hello(String name, Integer age) {
     validation
.required(name);
     validation
.required(age);
     validation
.min(age, 0);
     
     
if(validation.hasErrors()) {                
         
for(Error error : validation.errors()) {
             
System.out.println(error.message());
         
}
     
}
}

      假定name和age都沒有賦值,控制檯將會打印以下信息:

Required
Required

      這是因爲在$PLAY_HOME/resources/messages文件中定義了默認的消息:

validation.required=Required

      Play提供了三種方式自定義驗證消息:

  1. 在項目的conf/messages文件中自定義消息,覆蓋默認的message消息。
  2. 直接將自定義的驗證消息作爲參數傳遞給驗證器,覆蓋默認的message消息。
  3. 定義帶參數的驗證消息,參數通常爲error對象的key值。


本地化的驗證消息

      我們可以配置應用程序的conf/messages文件,採用統一的驗證消息匹配error對象的message消息,這是最簡單的覆蓋消息的寫法:

validation.required = Please enter a value 
      經過以上配置後,任何不通過validation.required驗證的消息都是Please enter a value。


帶參數的驗證消息

      %s佔位符將被替換成error對象的key:

validation.required=%s is required

      結合以上例子,輸出結果爲:

name is required
age
is required

      框架默認將被驗證的參數的名字作爲error對象的key值進行傳遞,但也有其他方法修改error對象的key值。比如,針對上例中hello方法(Action)的name參數進行本地化處理:

name = Customer name

      結果就會變成:

Customer name is required
age
is required

      也可以使用error.message(String key)方法直接覆蓋error對象的key值:

Error error = validation.required(name).error;
if(error != null) {
   
System.out.println(error.message("Customer name"));
}

      Play內置了很多驗證方法,也提供了不同的消息參數。比如match驗證,在消息表達式中定義了字符串類型的參數,與前面介紹的%s的佔位符不同,其規定的參數索引爲2:

validation.match=Must match %2$s

      類似的,range驗證定義了兩個整數型參數,以2和3作爲索引:

validation.range=Not in the range %2$d through %3$d 

      讀者可以在$PLAY_HOME/resources/messages文件中查看其他的驗證以及所含的參數。


補充:

讀者可能會有疑問,爲什麼這些參數的索引都是從2開始的。因爲match,range,minSize等驗證都是需要比較的,框架規定將索引爲1的參數設置爲比較源。


自定義本地化驗證消息

      Play內置驗證器的message消息,是在$PLAY_HOME/resources/messages文件中定義的。我們也可以在項目中定義自己的驗證消息:

validation.required.em = You must enter the %s! 

      在Action方法中,就可以採用自己定義的驗證消息機制,手動進行驗證:

validation.required(manualKey).message("validation.required.em"); 

      我們也可以把自己定義的驗證消息機制作爲註解中message的參數使用:

public static void hello(@Required(message="validation.required.em") String name) { 
   
...
}

      同理,JavaBean的屬性中也可以使用這種驗證技術(註解在JavaBean的屬性前):

public class Person extends Model { 
   
@Required(message = "validation.required.em")
   
public String name;
   
...
}
public static void hello(@Valid Person person) { 
   
...
}


更靈活的自定義方式

      Play還提供了一種非常靈活的方式實現驗證消息的自定義,直接在代碼中爲驗證器標註message消息:

validation.required(manualKey).message("Give us a name!"); 

      在註解中的使用方法:

public static void save(@Required(message = "Give us a name!") String name) { 
   
...
}

      在JavaBean中的使用方法:

public class Person extends Model {
   
@Required(message = "Give us a name!")
   
public String name;
   
...
}
public static void save(@Valid Person person) { 
   
...
}

1.3 模板中顯示錯誤信息#

      如果需要將驗證的錯誤信息在視圖模板中顯示,Play提供的#{ifErrors}標籤和error對象可以輕鬆完成這些工作。在hello Action中進行數據驗證,並使用默認的hello.html模板顯示:

public static void hello(String name, Integer age) {
     validation
.required(name);
     validation
.required(age);
     validation
.min(age, 0);
     render
(name, age);
}

      編輯view/Application/hello.html模板內容,使用#{ifErrors}標籤顯示驗證結果信息:

#{ifErrors}
 
   
<h1>Oops...</h1>
 
   
#{errors}
       
<li>${error}</li>
   
#{/errors}
 
#{/ifErrors}
#{else}
 
   
Hello ${name}, you are ${age}.
 
#{/else}

      顯然,以上的代碼並不能滿足真實應用的需求。因爲在實際項目中,如果驗證失敗了就應該顯示原來的表單,並提示用戶請再次輸入。針對這個需求,我們需要定義兩個Action:一個用於顯示錶單,另一個用於處理POST,數據的驗證將發生在後一個操作。當驗證有錯誤發生時,保存錯誤信息並重定向到第一個Action。Play提供的validation.keep()方法可以在下一個請求中保持錯誤信息的集合。以下是真實項目示例:

public class Application extends Controller {
 
   
public static void index() {
      render
();
   
}
 
   
public static void hello(String name, Integer age) {
      validation
.required(name);
      validation
.required(age);
      validation
.min(age, 0);
     
if(validation.hasErrors()) {
         
params.flash();     // 將HTTP參數保存在Flash作用域中
          validation
.keep();  // 在下一個請求中保持錯誤信息的集合
          index
();
     
}
      render
(name, age);
   
}
 
}

      修改view/Application/index.html模板:

#{ifErrors}
   
<h1>Oops…</h1>
 
   
#{errors}
       
<li>${error}</li>
   
#{/errors}
#{/ifErrors}
 
#{form @Application.hello()}
   
<div>
     
Name: <input type="text" name="name" value="${flash.name}" />
   
</div>
   
<div>
     
Age: <input type="text" name="age" value="${flash.age}" />
   
</div>
   
<div>
     
<input type="submit" value="Say hello" />
   
</div>
#{/form}

      爲了提供更好的用戶體驗,我們可以將錯誤信息顯示在未通過驗證的字段旁:

#{ifErrors}
   
<h1>Oops…</h1>
#{/ifErrors}
 
#{form @Application.hello()}
   
<div>
     
Name: <input type="text" name="name" value="${flash.name}" />
     
<span class="error">#{error 'name' /}</span>
   
</div>
   
<div>
     
Age: <input type="text" name="age" value="${flash.age}" />
     
<span class="error">#{error 'age' /}</span>
   
</div>
   
<div>
     
<input type="submit" value="Say hello" />
   
</div>
#{/form}


1.4 註解方式#

      註解封裝於play.data.validation包中,它採用與每個驗證對象一一對應的方式,實現有選擇性的,更加直觀的數據約束。在使用數據驗證的時候,只需要對控制器中的方法參數添加註解即可:

public static void hello(@Required String name, @Required @Min(0) Integer age) {
   
if(validation.hasErrors()) {
       
params.flash();    // 將HTTP參數保存在Flash作用域中
       validation
.keep(); // 在下一個請求中保持錯誤信息的集合
       index
();
   
}
   render
(name, age);
}
      相比之下,使用註解的代碼就顯得整潔的多了。


1.5 模型對象的驗證#

      在Play中通常採用註解的形式爲模型對象添加數據驗證。重寫前面所介紹的例子,爲User實體的name屬性添加@Required驗證註解,age屬性添加@Required和@Min(0)驗證註解:

package models;
 
public class User {
   
   
@Required
   
public String name;
 
   
@Required
   
@Min(0)
   
public Integer age;
}

      接着修改hello Action方法,使用@Valid註解對模型對象進行數據驗證:

public static void hello(@Valid User user) {
   
if(validation.hasErrors()) {
       
params.flash();    // 將HTTP參數保存在flash作用域中
       validation
.keep(); // 在下一個request中保持錯誤信息的集合
       index
();
   
}
   render
(user);
}

      最後修改index.html表單,如果驗證有錯,在頁面上顯示提示信息:

#{ifErrors}
   
<h1>Oops...</h1>
#{/ifErrors}
 
#{form @Application.hello()}
   
<div>
     
Name: <input type="text" name="user.name" value="${flash['user.name']}" />
     
<span class="error">#{error 'user.name' /}</span>
   
</div>
   
<div>
     
Age: <input type="text" name="user.age" value="${flash['user.age']}" />
     
<span class="error">#{error 'user.age' /}</span>
   
</div>
   
<div>
     
<input type="submit" value="Say hello" />
   
</div>
#{/form}

1.6 內置驗證#

      在框架的play.data.validation包中,系統已經定義了若干內置的驗證機制,這些常用的驗證如表1所示:

(表1 內置驗證)

名稱 驗證內容 名稱 驗證內容
email 驗證E-mail地址是否合法 equals 驗證兩個值是否相等
future 驗證是否爲相對未來的時間 isTrue 驗證String或者Boolean類型變量是否爲true
max 驗證數值大小是否大於給定的值 maxSize 驗證字符串長度是否大於給定的值
min 驗證數值大小是否小於給定的值 minSize 驗證字符串長度是否小於給定的值
match 驗證是否匹配給定的正則表達式 past 與future相對,驗證是否爲相對過去的時間
range 驗證是否在給定的兩個數值範圍內 required 驗證是否爲空
url 驗證是否爲合法的URL phone 驗證是否爲合法的電話號碼
ipv4Address 驗證是否爲符合ipv4規則的IP地址 ipv6Address 驗證是否爲符合ipv6規則的IP地址

   其中,required、min以及range驗證器在前幾節介紹的示例中已經有所涉及。本小節將着重介紹其中的email、match與phone驗證器。除此之外的幾類驗證器都非常簡單實用,就不再做過多的敘述了。

email

      使用email驗證器可以對給定的E-mail地址是否正確進行驗證,其message key爲validation.email。下面通過示例演示如何使用email驗證器對給定的E-mail地址進行檢測。

validation.email(address);

      對變量address進行email驗證,如果address不是有效的E-mail地址,驗證器會將錯誤以play.data.validation.Error的形式保存。email驗證同樣也支持註解用法,讀者在定義model時可以爲模型的屬性添加@Email註解:

@Email String address

match

      match驗證器是相對比較特別的驗證器,該驗證器可以使用正則表達式(用於描述或者匹配一系列符合某個句法規則的字符串的單個字符串)作爲校驗規則。在使用match驗證器時,需要制定充當驗證規則的正則表達式字符串作爲參數,注意空字符串將會被認爲是合法的,其message key爲validation.match。下面通過示例演示如何使用match驗證器對給定的字符串進行檢測。

validation.match(abbreviation, "[A-Z]{3}"); // TLA

      對變量abbreviation進行match驗證,如果abbreviation不能匹配正則表達式,驗證器會將錯誤以play.data.validation.Error的形式保存。match驗證同樣也支持註解用法,讀者在定義model時可以爲模型的屬性添加@match註解:

@Match("[A-Z]{3}") String abbreviation


phone

      phone驗證器可以對電話號碼進行校驗,驗證是否合法。在使用phone驗證器時需要注意,空字符串也將被認爲是合法的,其message key爲 validation.phone。下面通過示例演示如何使用phone驗證器對給定的電話號碼進行檢測。

validation.phone(value);

      對變量value進行phone驗證,如果value不是合法的電話號碼,驗證器會將錯誤以play.data.validation.Error的形式保存。phone驗證同樣也支持註解用法,讀者在定義model時可以爲模型的屬性添加@phone註解:

@Phone String phone
      讀者在使用時需要注意,phone驗證器的底層實現是基於電話號碼模式匹配的,其準確性並不是特別高。如果希望實現準確的電話號碼驗證器,推薦使用@match驗證器制定不同國家的電話號碼驗證方式。電話號碼的自定義格式可以是+CCC (SSSSSS)9999999999xEEEE,其中:
  • +是可選的,代表國家碼。
  • CCC是可選的,代表前3位國家代碼,需要注意的是其後必須要有一個分隔符。
  • (SSSSSS)是可選的,代表6位地區碼。
  • 9999999999是必須,表示電話號碼,最高爲20位(已覆蓋了所知的情況和未來可能的號碼)。
  • x是可選的,表示擴展,也可以寫成“ext”或“extension”。
  • EEEE是可選的,代表擴展碼,最多4位。
  • 分隔符可以使用空格、‘-’、‘.’或是‘/’其中的一個,並且可以在號碼的任意地方使用。

      下面是不同國家電話號碼的樣例,提供給讀者們參考:

  • 中國:+86 (10)69445464
  • 美國:(305) 613 09 58 ext 101
  • 法國:+33 1 47 37 62 24 x3
  • 德國:+49-4312 / 777 777
  • 英國:(020) 1234 1234

1.7 自定義驗證#

      自定義的驗證器可以通過@CheckWith註解進行綁定:

public class User {
   
   
@Required
   
@CheckWith(MyPasswordCheck.class)
   
public String password;
   
   
static class MyPasswordCheck extends Check {
       
       
public boolean isSatisfied(Object user, Object password) {
           
return notMatchPreviousPasswords(password);
       
}
   
}
}

注意:

自定義驗證器時不要忘記加載play.data.validation包。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章