Sealyu

          --- 博客已遷移至: http://www.sealyu.com/blog

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            618 隨筆 :: 87 文章 :: 225 評(píng)論 :: 0 Trackbacks
          文檔選項(xiàng)

          未顯示需要 JavaScript 的文檔選項(xiàng)

          將打印機(jī)的版面設(shè)置成橫向打印模式

          打印本頁(yè)

          將此頁(yè)作為電子郵件發(fā)送

          將此頁(yè)作為電子郵件發(fā)送


          級(jí)別: 初級(jí)

          胡 鍵 (jianhgreat@hotmail.com), 西安交通大學(xué)碩士

          2007 年 4 月 12 日

          Valang 是 Validation Language 的縮寫(xiě),Valang Validator 的字面含義就是“使用驗(yàn)證語(yǔ)言的驗(yàn)證器”,是一種支持聲明的驗(yàn)證器。本文詳細(xì)的介紹了 ValangValidator 的使用和相關(guān)所需的配置。作為一種新型的驗(yàn)證工具,它提供的驗(yàn)證語(yǔ)言(valang)具有簡(jiǎn)單、易學(xué),易擴(kuò)展等特點(diǎn)。

          Valang 是 Validation Language 的縮寫(xiě),Valang Validator 的字面含義就是“使用驗(yàn)證語(yǔ)言的驗(yàn)證器”,它是一種支持聲明的驗(yàn)證器。作為新出的驗(yàn)證工具,它擁有不同于現(xiàn)有其它相似工具的特點(diǎn):

          • 基于聲明的驗(yàn)證工具包,大多數(shù)情況下可以避免手工書(shū)寫(xiě)代碼。
          • 提供了一套精巧的 DSL(Domain Specific language),支持表達(dá)式語(yǔ)言。語(yǔ)法非常簡(jiǎn)潔,且支持?jǐn)U展方便。
          • 配置精簡(jiǎn),可讀性高、易維護(hù)。
          • 包含在 Spring Module 中,簡(jiǎn)化了 Spring Framework 的 Validator 使用。
          • 支持 JavaScript,對(duì)于 Web 應(yīng)用,支持客戶(hù)端的驗(yàn)證。

          本文將帶領(lǐng)讀者對(duì)以上特點(diǎn)一一尋訪(fǎng),在進(jìn)一步閱讀本文之前,請(qǐng)確保讀者具有 Spring Framework 的使用經(jīng)驗(yàn),如果有 Spring MVC 的經(jīng)驗(yàn)更佳。

          使用概覽

          Valang Validator 屬于 Spring Module 的 Validation 模塊。Spring Module 是一組擴(kuò)展 Spring Framework 功能的工具集,當(dāng)前的版本是 0.6。它作為獨(dú)立于 Spring Framework 的項(xiàng)目而存在,其目的非常單純:方便 Spring Framework 與其它框架的集成,避免為此修改 Spring Framework 的核心。

          使用 Valang Validator 必須先了解它的兩個(gè)重要組件:

          • valang,即驗(yàn)證語(yǔ)法,使用者使用它將約束定義轉(zhuǎn)化為 ValangValidator 能解析的驗(yàn)證規(guī)則。
          • ValangValidator,它是 Spring Validator 接口的具體實(shí)現(xiàn)。負(fù)責(zé)驗(yàn)證規(guī)則解析,完成實(shí)際的驗(yàn)證工作。

          了解了這些關(guān)于 Valang Validator 的信息之后,不妨從一個(gè)例子來(lái)了解它的使用。本例使用的環(huán)境:JDK 1.5、Spring Framework2.0 和 Spring Module 0.6。

          問(wèn)題描述

          為 了盡可能的體現(xiàn) Valang Validator 的特點(diǎn),例子中需要驗(yàn)證的對(duì)象有意地被設(shè)計(jì)得稍微有些復(fù)雜:首先,屬性是最常見(jiàn)的 3 種類(lèi)型,而且其中還有一個(gè)是在 Java 中有些煩人的Date;其次,它還包含了一個(gè)成員類(lèi)屬性。關(guān)于類(lèi)結(jié)構(gòu)和相關(guān)的約束如下表:

          類(lèi)結(jié)構(gòu)約束
          public class User { private String name; private int age; private Date birthday; private Address address; …… } public class Address { private String state; private String town; private String street; …… } name,必填,且長(zhǎng)度不超過(guò) 50。
          ? age,非必填,必須大于等于 0,小于等于 60。
          ? birthday,非必填,必須在 2000-01-01 之后。
          ? address.state,必填,長(zhǎng)度不超過(guò) 50。
          ? address.town,必填,長(zhǎng)度不超過(guò) 50。
          ? address.street,必填,長(zhǎng)度不超過(guò) 200。
          ?

          實(shí)現(xiàn) Validator

          實(shí) 現(xiàn) Valang Validator 就是要完成兩部分工作:定義 valang 描述和 Validator 創(chuàng)建。Validator 的創(chuàng)建可以使用 Java 代碼進(jìn)行,也可使用 Spring DI 完成。本例采用后一種方法,Bean 定義如下(文件名:valang.xml):

          <bean id="userValidator" class="org.springmodules.validation.valang.ValangValidator">
          <property name="valang">
          <value>
          <![CDATA[
          { name : ? is not null and ? has text and length(?)<= 50
          : 'Name is empty or too long.'}
          { age : ? between 0 and 60 : 'Age should between 0 and 60.'}
          |-------10--------20--------30--------40--------50--------60--------70--------80--------9|
          |-------- XML error: The previous line is longer than the max of 90 characters ---------|
          { birthday : ? is null or ? >[20000101]
          : 'Birthday should be after 2000-01-01.'}
          { address : ? is not null : 'Address is empty.'}
          { address.state : address is null or ( ? is not null and
          ? has text and length(?)<=50 )
          : 'state is empty or too long.'}
          { address.town : address is null or ( ? is not null and
          ? has text and length(?)<=50 )
          : 'town is empty or too long.'}
          { address.street : address is null or ( ? is not null and
          ? has text and length(?)<=50 )
          : 'street is empty or too long.'}
          ]]>
          </value>
          </property>
          <property name="dateParsers">
          <map>
          <entry key="^""d{8}$" value="yyyyMMdd" />
          </map>
          </property>
          </bean>

          瞧,沒(méi)有一行 Java 代碼,居然完成了一個(gè) Validator 的定義。這其中的秘密就是 valang 屬性,它定義了驗(yàn)證規(guī)則。再看看它的語(yǔ)法,是不是很酷?!另一個(gè)屬性 dateParsers 定義了日期轉(zhuǎn)換的格式。關(guān)于它們的解釋在后面有詳細(xì)的介紹。

          技巧:

          如 果 Adress 對(duì)象被多個(gè)對(duì)象引用,且對(duì)于 Address 的驗(yàn)證約束相同,那么以上的 address 相關(guān)的約束規(guī)則就需要重復(fù)書(shū)寫(xiě)。這樣,既繁且不利于維護(hù)。此時(shí),可以將 address 相關(guān)的約束組織成一個(gè) validator,實(shí)現(xiàn)約束的復(fù)用。具體步驟:

          a) 抽取 Address 的約束到 addressValidator。

          <bean id="addressValidator" class="org.springmodules.validation.valang.ValangValidator">
          <property name="valang">
          <value>
          <![CDATA[
          { state : ? is not null and ? has text and length(?)<=50
          : 'state is empty or too long.'}
          { town : ? is not null and ? has text and length(?)<=50
          : 'town is empty or too long.'}
          { street : ? is not null and ? has text and length(?)<=50
          : 'street is empty or too long.'}
          ]]>
          </value>
          </property>
          </bean>

          b) 實(shí)現(xiàn)繼承 ValangValidator 的 UserValidator,包含 addressValidator:

          public class UserValidator extends ValangValidator {
          private Validator addressValidator;

          public void setAddressValidator(Validator addressValidator) {
          this.addressValidator = addressValidator;
          }

          public void validate(Object target, Errors errors){
          super.validate( target, errors);
          Address address= ((User)target).getAddress();
          // address 為 null 時(shí),不進(jìn)行進(jìn)一步的驗(yàn)證。
          if( null!= address){
          //注意與 popNestedPath 成對(duì)使用
          errors.pushNestedPath("address");
          //驗(yàn)證 address
          addressValidator.validate( address, errors);
          errors.popNestedPath();
          }
          }
          }

          c) 修改 userValidator 的配置。

          <bean id="userValidator" class="包名.UserValidator">
          <property name="addressValidator" ref="addressValidator"/>
          <property name="valang">
          <value>
          <![CDATA[
          { name : ? is not null and ? has text and length(?)<= 50
          : 'Name is empty or too long.'}
          { age : ? between 0 and 60 : 'Age should between 0 and 60.'}
          { birthday : ? is null or ? >[20000101] : 'Birthday should be after 2000-01-01.'}
          |-------10--------20--------30--------40--------50--------60--------70--------80--------9|
          |-------- XML error: The previous line is longer than the max of 90 characters ---------|
          { address : ? is not null : 'Address is empty.'}
          ]]>
          </value>
          </property>
          <property name="dateParsers">
          <map>
          <entry key="^""d{8}$" value="yyyyMMdd" />
          </map>
          </property>
          </bean>

          單元測(cè)試

          對(duì) ValangValidator 進(jìn)行單元測(cè)試非常簡(jiǎn)單,需要注意的是它的實(shí)例化。這里使用 BeanFactory 來(lái)創(chuàng)建待測(cè)的 Validator。Spring 提供了測(cè)試用例基類(lèi) AbstractDependencyInjectionSpringContextTests,使得手工創(chuàng)建完全避免:

          public class UserValidatorTest extends AbstractDependencyInjectionSpringContextTests {
          private Validator userValidator;

          public UserValidatorTest(){
          super();
          //缺省是AUTOWIRE_BY_TYPE,當(dāng)Bean文件有2個(gè)以上的同類(lèi)型
          //Bean定義時(shí)就應(yīng)該采用AUTOWIRE_BY_NAME方式。
          setAutowireMode(AUTOWIRE_BY_NAME);
          }

          protected String[] getConfigLocations() {
          //指明配置文件的位置
          return new String[]{"file:文件路徑/valang.xml"};
          }
          //基類(lèi)調(diào)用該方法完成依賴(lài)注入(DI)
          public void setUserValidator(Validator userValidator) {
          this.userValidator = userValidator;
          }

          public void testEmptyObject(){
          User user= new User();
          BindException errors= new BindException(user, "target");
          userValidator.validate( user, errors);
          assertTrue( errors.getAllErrors().size()== 2);
          ObjectError error1= (ObjectError)(errors.getAllErrors().get(0));
          assertEquals(error1.getDefaultMessage(), "Name is empty or too long.");
          ObjectError error2= (ObjectError)(errors.getAllErrors().get(1));
          assertEquals(error2.getDefaultMessage(), "Address is empty.");
          }
          ……
          }

          使用 Validator

          上面的單元測(cè)試已經(jīng)展示了 ValangValidator 的使用,除此之外,也可使用 BeanFactory 將它注入某個(gè)控制器中使用:

          <bean id="userController" class="……">
          ……
          <property name="validator" ref="userValidator"/>
          </bean>

          從上面的例子來(lái)看,ValangValidator 的使用相當(dāng)簡(jiǎn)單。尤其對(duì)于有過(guò) Spring 經(jīng)驗(yàn)的用戶(hù)來(lái)說(shuō),除了需要掌握 valang 語(yǔ)法之外,其余的根本就和 Spring Validator 使用一樣。而且,例子中 valang 的語(yǔ)法所展示的簡(jiǎn)潔、直觀給人留下了深刻的印象。





          回頁(yè)首


          Valang 語(yǔ)法

          很 酷!這是我看到 valang 的第一印象。Spring 數(shù)據(jù)綁定語(yǔ)法相同的屬性表達(dá)式,靈活、便捷、易學(xué)的語(yǔ)法。這些,都使得另一個(gè)被廣泛使用的聲明性驗(yàn)證工具 Commons Validator 顯得相形見(jiàn)絀。雖然作為早期的聲明性驗(yàn)證工具的實(shí)現(xiàn),Commons Validator 的功績(jī)不可磨滅,但是其配置的繁瑣也使人厭煩不已。

          “以數(shù)據(jù)為中心”是 ValangValidator 的設(shè)計(jì)思路,此處的數(shù)據(jù)就是 valang 描述。valang 由一系列的規(guī)則組成,對(duì)于每個(gè)規(guī)則,它的結(jié)構(gòu)如下:

          { <key> : <predicate_expression> : <message> [: <error_code> [: <args> ]] }

          其中:

          • <key>,屬性表達(dá)式。它是要驗(yàn)證的屬性名,必填。
          • <predicate_expression>,謂詞表達(dá)式。期望滿(mǎn)足的條件,必填。
          • <message>,缺省消息。當(dāng)不滿(mǎn)足預(yù)期表達(dá)式時(shí)顯示的消息,必填。
          • <error_code>,錯(cuò)誤碼。國(guó)際化時(shí)使用的消息鍵值,當(dāng)它存在時(shí)缺省消息被忽略,可選。
          • <args>,參數(shù)。逗號(hào)分隔的參數(shù)值,用于替換錯(cuò)誤碼中定義的站位符,可選。

          這些參數(shù),除了謂詞表達(dá)式之外,都可以在 Spring 中見(jiàn)到它們的蹤影。而它們的含義、用法也絲毫沒(méi)有發(fā)生變化。因此,讓我們把關(guān)注的焦點(diǎn)集中在謂詞表達(dá)式上。

          表達(dá)式語(yǔ)法

          valang 將謂詞表達(dá)式定義成如下結(jié)構(gòu):

          <expression> ::= <expression> ( ( "AND" | "OR" ) <expression> )+ | <predicate>

          其中的謂詞(<predicate>),是由操作符、文字常量、bean 屬性、數(shù)學(xué)表達(dá)式和函數(shù)組成的可計(jì)算實(shí)體。

          1.操作符

          valang 的操作符有三種:一元操作符,二元操作符和其他操作符。支持的操作符如下表:

          類(lèi)型 支持類(lèi)型 操作符 說(shuō)明
          一元 對(duì)象 NULL | IS NULL 為空
          NOT NULL | IS NOT NULL 非空
          字符串 HAS TEXT 包含非空格的字符
          HAS NO TEXT null或只有空格字符
          HAS LENGTH | IS NOT BLANK 長(zhǎng)度>0
          HAS NO LENGTH | IS BLANK null或長(zhǎng)度=0
          IS UPPERCASE | IS UPPER CASE |
          IS UPPER
          所有字母大寫(xiě)
          IS NOT UPPERCASE |
          IS NOT UPPER CASE |
          IS NOT UPPER
          不是所有字母大寫(xiě)
          IS LOWERCASE | IS LOWER CASE | IS LOWER 所有字母小寫(xiě)
          IS NOT LOWERCASE |
          IS NOT LOWER CASE |
          IS NOT LOWER
          不是所有字母小寫(xiě)
          IS WORD
          IS NOT WORD
          二元 字符串、日期、布爾和數(shù)字 = | == | IS | EQUALS 相等
          != | <> | >< | IS NOT | NOT EQUALS 不等
          數(shù)字、日期 >|GREATER THAN |
          IS GREATER THAN
          大于
          < | LESS THAN | IS LESS THAN 小于
          >= | => |
          GREATER THAN OR EQUALS |
          IS GREATER THAN OR EQUALS
          大于等于
          <= | =< | LESS THAN OR EQUALS | IS LESS THAN OR EQUALS 小于等于
          其他 數(shù)字、日期 BETWEEN A AND B 大于等于A,且小于等于B
          NOT BETWEEN A AND B 小于A,或大于B
          字符串、日期、布爾和數(shù)字 IN <value> (, <value>)* 在值列表中
          NOT IN <value> (, <value>)* 不在值列表中
          布爾表達(dá)式 NOT 取反

          這些操作符是大小寫(xiě)非敏感的,另外在使用時(shí)需要注意:

          • 避免在驗(yàn)證時(shí)出現(xiàn) NullException,這一點(diǎn)可以在 userValidator 的例子中看到。
          • 二元操作符兩邊的操作數(shù)類(lèi)型必須一致。
          ?

          2.文字常數(shù)

          Valang 支持 4 種文字常數(shù):字符串、數(shù)字、日期和布爾。這四種類(lèi)型的文字常數(shù)如下表:

          類(lèi)型 說(shuō)明
          字符串 字符串使用單引號(hào)包含,如 ’foxgem’。
          數(shù)字 數(shù)字被轉(zhuǎn)換為 java.math.BigDecimal。
          布爾 布爾值必須是:TRUE、YES、FALSE、NO 中的一個(gè),且不要使用單引號(hào)。
          日期 日期值使用 [] 包含,如 [2000-01-01]。

          valang 對(duì)于日期類(lèi)型的處理非常特殊,而且有趣:

          • 使用 T 來(lái)代表當(dāng)前時(shí)間, 如 [T]。
          • 提供了移位操作,語(yǔ)法是:日期 > 時(shí)間單位,日期 < 時(shí)間單位。前者表示將日期中對(duì)應(yīng)的單位部分向后滾動(dòng)到該時(shí)間單位的最大值;后者表示將日期中對(duì)應(yīng)的單位部分向前滾動(dòng)到該時(shí)間單位的最小值。支持的時(shí)間單位和對(duì)應(yīng)范圍:

            1) 秒(s):0~999 毫秒
            2) 分鐘(m):0秒0毫秒 ~ 59秒999毫秒
            3) 小時(shí)(H):0分0秒0毫秒 ~ 59分59秒999毫秒
            4) 天(d):0時(shí)0分0秒0毫秒 ~ 23時(shí)59分59秒999毫秒
            5) 星期(w):周一0時(shí)0分0秒0毫秒 ~ 周日23時(shí)59分59秒999毫秒
            6) 月(M):月第一天0時(shí)0分0秒0毫秒 ~ 月最后一天23時(shí)59分59秒999毫秒
            7) 年(y):年第一天0時(shí)0分0秒0毫秒 ~ 年最后一天23時(shí)59分59秒999毫秒

            如:[T>d],表示當(dāng)天的 23 時(shí) 59 分 59 秒 999 毫秒。

          • 支持日期的增減運(yùn)算。增減分別對(duì)應(yīng):“+”和“-”;數(shù)量對(duì)應(yīng):i 時(shí)間單位,i 表示整數(shù)。如:[T>d-1d+2H],表示先將當(dāng)前時(shí)間后滾至當(dāng)天的最后時(shí)刻,再減去一天,再加上 2 個(gè)小時(shí)。

            日期格式在 valang 中也有很好的支持,可以很方便的定義自己的格式。日期解析過(guò)程如下:

            1) 取出日期字符串,在 dateParsers 中定義 Map 的正則表達(dá)式鍵集合中進(jìn)行匹配。
            2) 當(dāng)找到符合的正則表達(dá)式時(shí),以它為鍵值獲取對(duì)應(yīng)的日期格式串。
            3) 使用 SimpleDateFomat,將日期字符串按照日期格式串轉(zhuǎn)化為對(duì)應(yīng)的 Date 類(lèi)型。

            因此,如果要在 valang 中使用 yyyy-MM-dd 的格式時(shí),那么使用以下的定義:

            <entry key="^""d{4}-""d{2}-""d{2}$" value="yyyy-MM-dd" />

          3. bean 屬性

          bean 屬性的用法和屬性表達(dá)式的用法一樣,在運(yùn)行時(shí)它的值會(huì)被取出。另外,valang 使用“?”來(lái)表示在規(guī)則中定義的屬性表達(dá)式的值。

          4. 數(shù)學(xué)表達(dá)式

          valang 支持的數(shù)學(xué)運(yùn)算符有:+、-、*、/(或 div)、%(或 mod)。

          5.函數(shù)

          valang 內(nèi)置的函數(shù)如下:

          函數(shù) 說(shuō)明
          length | len | size |count 如果傳入?yún)?shù)是集合或數(shù)組,那么返回它們的大小;否則,返回傳入?yún)?shù)的 toString 所返回字符串的長(zhǎng)度。
          使用例子:length(?)<50
          match | matches 傳入?yún)?shù)(參數(shù) 2)的 toString 返回的字符串是否匹配給定的正則表達(dá)式(參數(shù)1)。
          使用例子:match('^""d{2}$', ?) is true
          email 傳入?yún)?shù)的 toString 返回的字符串是有效的 email。
          使用例子:email(?) is true
          upper 將傳入?yún)?shù)的 toString 返回的字符串轉(zhuǎn)換為大寫(xiě)。
          使用例子:upper(?)=='VALANG'
          lower 將傳入?yún)?shù)的 toString 返回的字符串轉(zhuǎn)換為小寫(xiě)。
          使用例子:lower(?)=='valang'
          ! 布爾值取反
          使用例子:!(?) is true。

          注意:它不能內(nèi)嵌表達(dá)式,如“!(length(?)<= 50) is true”將會(huì)出錯(cuò)。
          resolve 解析消息碼。
          inRole 傳入?yún)?shù)是角色名,如果當(dāng)前用戶(hù)有這個(gè)角色,則返回 true。

          valang 對(duì)于函數(shù)的支持并不是只僅僅提供幾個(gè)簡(jiǎn)單的函數(shù),它還允許用戶(hù)自定義函數(shù)。這大大的擴(kuò)展了它的表現(xiàn)力。





          回頁(yè)首


          自定義函數(shù)

          org.springmodules.validation.valang.functions.AbstractFunction 是 valang 函數(shù)的基類(lèi),所有的內(nèi)置函數(shù)都是它的子類(lèi),自定義函數(shù)也必須從它繼承。AbstractFunction 有幾個(gè)重要的方法需要注意:

          • 構(gòu)造函數(shù),子類(lèi)必須實(shí)現(xiàn),不要忘了先調(diào)用 super。
          • definedExactNumberOfArguments、 definedMaxNumberOfArguments、definedMinNumberOfArguments,分別是定義函數(shù)的參數(shù)個(gè)數(shù),函數(shù)的 最大參數(shù)個(gè)數(shù)、函數(shù)的最小參數(shù)個(gè)數(shù)。它們?cè)跇?gòu)造函數(shù)中被調(diào)用。
          • init,在所有屬性被設(shè)置后調(diào)用,負(fù)責(zé)初始化。
          • isAutowireByName 和 isAutowireByType,分別指定了函數(shù)的屬性被 BeanFactory 自動(dòng)裝配采用的方法。
          • doGetResult,返回函數(shù)的結(jié)果。

          下面通過(guò)實(shí)現(xiàn)一個(gè)自定義版本的 length 函數(shù)來(lái)了解自定義函數(shù)的編寫(xiě)和配置:

          函數(shù)定義:

          public class AnotherLengthFunction extends AbstractFunction {

          public AnotherLengthFunction(Function[] functions, int line, int column) {
          super(functions, line, column);
          //AnotherLengthFunction只支持一個(gè)參數(shù)
          definedExactNumberOfArguments(1);
          }

          protected Object doGetResult(Object target) throws Exception {
          //返回參數(shù)1的toString的值。如果有多個(gè)參數(shù),那么:
          //參數(shù)2:getArguments()[1].getResult(target).toString();以此類(lèi)推。
          //另外getArguments()的個(gè)數(shù)就是實(shí)際傳入的參數(shù)個(gè)數(shù)。
          String value= getArguments()[0].getResult(target).toString();
          return new Integer(value.length());
          }

          }

          函數(shù)配置:

          <property name="customFunctions">
          <map>
          <!-- 前者是函數(shù)使用的名字,后者對(duì)應(yīng)函數(shù)實(shí)現(xiàn)的class。
          使用例子:anotherLength(?)<50
          -->
          <entry key="anotherLength" value="AnotherLengthFunction"/>
          </map>
          </property>

          當(dāng)自定義的函數(shù)需要外部的資源,如數(shù)據(jù)庫(kù)連接、網(wǎng)絡(luò)連接、文件等,可以通過(guò) BeanFactory 自動(dòng)裝配來(lái)完成。此時(shí)需要覆蓋 isAutowireByName 或 isAutowireByType,指定自動(dòng)裝配的類(lèi)型,這兩個(gè)函數(shù)不能同時(shí)覆蓋。





          回頁(yè)首


          客戶(hù)端驗(yàn)證

          對(duì)于 Web 應(yīng)用程序來(lái)說(shuō),服務(wù)器的驗(yàn)證固然是必須的,但如果提供了客戶(hù)端驗(yàn)證的話(huà),那無(wú)疑是錦上添花的事情。幸運(yùn)的是,Valang Validator 已經(jīng)做到了這一點(diǎn)。要使用這一功能,需要先了解 2 個(gè)組件:

          1. org.springmodules.validation.valang.javascript.taglib.ValangRulesExportInterceptor

          這個(gè)攔截器的作用是輸出被當(dāng)前請(qǐng)求處理器所使用的 valang 驗(yàn)證規(guī)則到 ModelAndView 中,使 <validate> 可以利用這些信息。

          在以下情況下之一時(shí),該攔截器不做任何事情:

          • 當(dāng)被攔截的請(qǐng)求處理器不是 BaseCommandController。
          • 請(qǐng)求處理器使用的 Validator 不是 ValangValidator 實(shí)例。
          • 請(qǐng)求處理器不輸出一個(gè) Command 對(duì)象到 model 中。

          2. valang 標(biāo)簽庫(kù)

          valang 標(biāo)簽庫(kù)完成客戶(hù)端腳本的產(chǎn)生,并進(jìn)行實(shí)際的客戶(hù)端驗(yàn)證。它包含了 2 個(gè)標(biāo)簽:

          標(biāo)簽 作用 屬性 說(shuō)明
          codebase 產(chǎn)生被翻譯的 javascript 驗(yàn)證規(guī)則執(zhí)行所需要的代碼。它應(yīng)該每頁(yè)包含一次,且在被產(chǎn)生的驗(yàn)證規(guī)則之前。 includeScriptTags 非必須,指示產(chǎn)生的 javascript 代碼是否被 <script> 包含。
          fieldErrorsIdSuffix 非必須,指明用來(lái)包含屬性錯(cuò)誤信息的 <div> 或 <span> 的 id 的后綴。整個(gè) id 的最后組成:
          <屬性名 >< fieldErrorsIdSuffix >
          globalErrorsId 非必須,指明用來(lái)包含全局錯(cuò)誤信息的 <div> 或 <span> 的 id。
          validate 將valang規(guī)則翻譯成對(duì)應(yīng)的 javascript 驗(yàn)證規(guī)則。 commandName 非必須,指明需要驗(yàn)證的 command 對(duì)象名字。

          接下來(lái)我們以一個(gè)例子來(lái)展示如何應(yīng)用以上所學(xué)。例子本身并不復(fù)雜,就是在以上的 userValidator 例子的基礎(chǔ)上,完成一個(gè) Controller 和相關(guān)的 JSP 頁(yè)面。例子的步驟和說(shuō)明如下:

          1. Controller 負(fù)責(zé)初始化 User 對(duì)象,在提交時(shí)取出 User 各屬性的值放入到 ModelAndView 中。

          public class UserController extends SimpleFormController {

          protected Object formBackingObject(HttpServletRequest request){
          User user= new User();
          user.setAddress( new Address());
          return user;
          }

          protected ModelAndView onSubmit(Object command) throws Exception{
          User user=(User)command;
          ModelAndView mav= new ModelAndView(getSuccessView());
          //取出user的屬性,放到mav中。
          mav.addObject("isSubmit", "true");//提交處理的標(biāo)志。
          return mav;
          }

          protected void initBinder(HttpServletRequest request,
          ServletRequestDataBinder binder)
          throws Exception{
          SimpleDateFormat format= new SimpleDateFormat("yyyy-MM-dd");
          binder.registerCustomEditor(Date.class, "birthday"
          , new CustomDateEditor(format, true));
          }
          }

          2. JSP 頁(yè)面既充當(dāng) User 的編輯頁(yè)面,又是 Controller 提交后的屬性顯示頁(yè)面。它演示了 valang 標(biāo)簽庫(kù)的配置和使用,是本例的重頭戲。本例中使用了 Spring 2.0 中提供的 Spring Form 標(biāo)簽,關(guān)于它的使用請(qǐng)參見(jiàn) Spring 手冊(cè)。

          <%@ page contentType="text/html" %>
          <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
          <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
          <!-- 包含valang標(biāo)簽庫(kù) -->
          <%@ taglib prefix="valang" uri="http://www.springmodules.org/tags/valang" %>

          <html>
          <head>
          <title>User Detail</title>
          <!-- 請(qǐng)將codebase包含在<head>中,確保在頁(yè)面顯示時(shí),
          所需要的環(huán)境已經(jīng)產(chǎn)生 -->
          <valang:codebase includeScriptTags="true" fieldErrorsIdSuffix="_error"
          globalErrorsId="global_error"/>
          </head>

          <body>
          <c:if test="${not empty param.isSubmit}">
          <!-- 顯示提交之后的User各屬性值 -->
          </c:if>
          <c:if test="${empty param.isSubmit}">
          <form:form commandName="user" method="post">
          <!-- validate需要包含在<form>中,commandName
          是UserController的CommandName -->
          <valang:validate commandName="user"/>
          <div id="global_error">
          </div>
          <table>
          <tr>
          <td>name:</td>
          <td>
          <form:input path="name" />
          <!-- 注意id的命名。在_error之前的屬性名實(shí)際是
          在valang驗(yàn)證規(guī)則中出現(xiàn)的屬性表達(dá)式的名字。否則,將出錯(cuò)。
          如:以上的userValidator的例子中,如果將address相關(guān)的屬性
          合并到一個(gè)中:
          { address: ? is not null
          and address.state is not null and length(address.state)<= 50
          and address.town is not null and length(address.town)<= 50
          and address.street is not null and length(address.street)<= 50
          : 'Invalida address.'}
          那么,下面的 address 相關(guān)的屬性對(duì)應(yīng)的 <span> 的 id 就應(yīng)該是:
          address_error,且只有一個(gè)。因?yàn)槎鄠€(gè)時(shí),會(huì)引起 javascript 的錯(cuò)誤。
          -->
          <span id="name_error"><form:errors path="name"/></span>
          </td>
          </tr>
          <tr>
          <td>age:</td>
          <td>
          <form:input path="age" />
          <span id="age_error"><form:errors path="age" /></span>
          </td>
          </tr>
          <tr>
          <td>birthday:</td>
          <td>
          <form:input path="birthday" />
          <span id="birthday_error"><form:errors path="birthday" /></span>
          </td>
          </tr>
          <tr>
          <td>state:</td>
          <td>
          <form:input path="address.state" />
          <span id="address.state_error"><form:errors path="address.state" /></span>
          </td>
          </tr>
          <tr>
          <td>town:</td>
          <td>
          <form:input path="address.town" />
          <span id="address.town_error"><form:errors path="address.town" /></span>
          </td>
          </tr>
          <tr>
          <td>street:</td>
          <td>
          <form:input path="address.street" />
          <span id="address.street_error"><form:errors path="address.street" /></span>
          |-------10--------20--------30--------40--------50--------60--------70--------80--------9|
          |-------- XML error: The previous line is longer than the max of 90 characters ---------|
          </td>
          </tr>
          <tr>
          <td colspan="2">
          <input type="submit" value="Submit" />
          </td>
          </tr>
          </table>
          </form:form>
          </c:if>
          </body>
          </html>

          3. 相關(guān)配置。

          <bean id="handlerMapping" 
          class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
          <property name="interceptors">
          <list>
          <ref bean="valangInterceptor"/>
          </list>
          </property>
          <property name="urlMap">
          <map>
          <entry key="/userDetail.htm"><ref bean="userController"/></entry>
          </map>
          </property>
          </bean>

          <bean id="userController" name="/userDetail.htm" class="example.UserController">
          <property name="formView" value="userDetail"/>
          <property name="successView" value="redirect:/userDetail.htm"/>
          <property name="validator" ref="userValidator"/>
          <property name="commandName" value="user"/>
          <property name="commandClass" value="example.User"/>
          </bean>

          <bean id="valangInterceptor"
          class="org.springmodules.validation.valang.javascript.taglib.ValangRulesExportInterceptor"/>
          |-------10--------20--------30--------40--------50--------60--------70--------80--------9|
          |-------- XML error: The previous line is longer than the max of 90 characters ---------|

          <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
          <property name="prefix" value="/WEB-INF/jsp/"/>
          <property name="suffix" value=".jsp"/>
          <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
          </bean>





          回頁(yè)首


          結(jié)束語(yǔ)

          本 文詳細(xì)的介紹了 ValangValidator 的使用和相關(guān)所需的配置。作為一種新型的驗(yàn)證工具,它提供的驗(yàn)證語(yǔ)言(valang)具有簡(jiǎn)單、易學(xué),易擴(kuò)展等特點(diǎn)。由于采用驗(yàn)證語(yǔ)言,在大多數(shù)的情形 下,避免了書(shū)寫(xiě) Java 驗(yàn)證代碼的需要。這些特性,極大的簡(jiǎn)化了通常繁瑣的驗(yàn)證實(shí)現(xiàn)。與 Spring Framework 結(jié)合,也對(duì)于它的推廣有了很好的起點(diǎn)。同時(shí),在使用時(shí)也需注意,由于目前它的 1.0 版本尚未正式的發(fā)布,API 尚不穩(wěn)定。對(duì)于這一點(diǎn),使用者也需有心理準(zhǔn)備。



          參考資料



          關(guān)于作者


          胡 鍵,西安交通大學(xué)碩士,2000 年畢業(yè)后一直從事軟件開(kāi)發(fā)。2002 年開(kāi)始使用 Java,在平時(shí)的項(xiàng)目開(kāi)發(fā)中經(jīng)常采用 OpenSource 的工具,如 Ant、Maven、Hibernate、Struts 等,目前正在研究信息集成方面的規(guī)范和技術(shù)。可以通過(guò)jianhgreat@hotmail.com與他取得聯(lián)系,或訪(fǎng)問(wèn)個(gè)人blog:http://blog.donews.com/foxgem/

          posted on 2009-04-20 09:13 seal 閱讀(474) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): Spring
          主站蜘蛛池模板: 平陆县| 陇南市| 海兴县| 永安市| 金阳县| 都昌县| 西平县| 威宁| 九龙坡区| 通化市| 岢岚县| 乳山市| 舒城县| 新源县| 正安县| 永登县| 银川市| 津市市| 乐平市| 泾川县| 荣昌县| 两当县| 高陵县| 同江市| 磐石市| 恭城| 东丰县| 海宁市| 烟台市| 金寨县| 微博| 临安市| 大邑县| 宾阳县| 南皮县| 陆丰市| 鹿泉市| 太仆寺旗| 鸡西市| 东乌珠穆沁旗| 仙桃市|