用Validator(驗證器)提供的豐富的內建驗證方法簡化Struts的開發過程。
Struts架構的一個主要好處是它提供了對接收到的表單資料進行驗證的內建介面。如果有任何驗證失敗,則應用程式都會重新顯示HTML表單,這樣就可以改正無效的資料了。如果驗證成功,則處理過程會繼續進行。Struts架構的簡單驗證介面會減少與處理資料驗證有關的令人頭疼的事情,這樣你就可以把精力集中到驗證代碼上,而不是放到捕獲資料、重新顯示不完整或無效資料的技巧上。
但是,Struts內建的驗證介面也有缺點。例如,在整個應用程式中驗證代碼常常會大量重複,因為許多域需要相同的驗證邏輯。對一些相似欄位的驗證邏輯進行任何修改都要求在幾個地方修改代碼,還要重新編譯受影響的代碼。為瞭解決這個問題並增強Struts驗證介面的功能,作為Struts的第三方附加件建立了Validator架構。後來,Validator被整合到核心Struts程式碼程式庫中,並從Struts中分離出來,現在它是一個獨立的Jakarta Commons項目。雖然Validator是一個獨立的架構,但它仍能與其他程式封裝在一起後提供,並與Struts無縫整合。
Validator概述
沒有Validator,你就不得不編寫驗證表單資料所需的全部代碼,並把它放入Form Bean對象的validate( )方法中。對於想在其上進行資料驗證的每個Form Bean域來說,都需要編寫邏輯代碼來實現驗證。此外,你還必須編寫代碼來儲存驗證失敗時的出錯訊息。
有了Validator,你就不必在Form Bean中編寫用於驗證或儲存錯誤訊息的任何代碼。相反,Form Bean提供了Validator的一個ActionForm子類,它提供驗證或儲存錯誤訊息的功能。
可把Validator架構作為一個可用於Form Bean驗證的可插入的驗證例行程式系統來進行安裝。每個驗證例行程式都只是一個Java方法,負責執行特定類型的驗證任務,驗證可能通過,也可能失敗。 預設情況下,Validator與幾個有用的驗證例行程式封裝在一起來提供,這些例行程式能滿足大多數情況下的驗證要求。但是,如果Validator架構沒有提供你需要的驗證例行程式,那麼你可以自己建立定製的驗證例行程式,並將它插入到該架構中。此外,Validator還支援伺服器端和用戶端(JavaScript)的驗證,而Form Bean只提供伺服器端驗證介面。
Validator使用兩個XML設定檔來分別確定安裝哪個驗證例行程式和如何將它們應用於給定的應用程式。第一個設定檔validator-rules.xml說明應該被插入到架構中的驗證例行程式,並提供每個驗證的邏輯的名稱。validator-rules.xml檔案還定義了每個驗證例行程式的用戶端JavaScript代碼。可以配置Validator讓它把這個JavaScript代碼發送到瀏覽器上,這樣驗證就可以在用戶端和伺服器端進行了。
第二個設定檔validation.xml確定哪個驗證例行程式應用到哪個Form Bean。檔案中的定義使用struts-config.xml檔案給出的Form Bean的邏輯名稱以及validator-rules.xml檔案給出的驗證例行程式的邏輯名稱,以便把二者關聯起來。
使用Validator架構套件括啟用Validator外掛程式、配置Validator的兩個設定檔,以及建立提供Validator的ActionForm子類的Form Beans。下面詳細解釋如何配置和使用Validator。
啟用Validator外掛程式
雖然Validator架構是與Struts封裝在一起提供的,但在預設狀況下Validator並不被啟用。為了啟用Validator,要向你的應用程式的struts-config.xml檔案中添加下面的外掛程式定義。
<!-- Validator Configuration --><plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/WEB-INF/ validator-rules.xml, /WEB-INF/ validation.xml"/></plug-in>
該定義告訴Struts為你的應用程式載入並初始化Validator外掛程式。在初始化時,該外掛程式裝入由路徑名屬性指定的、用逗號分隔的Validator設定檔清單。每個設定檔的路徑應該用與Web應用程式的相關的路徑來指定,如前面的例子所示。
請注意,你的應用程式的struts-config.xml檔案必須與Struts Configuration Document Type Definition(Struts配置文件類型定義,DTD)一致,後者規定檔案中元素出現的順序。所以,你必須把Validator外掛程式定義放到該檔案的適當位置。確保檔案中元素適當排列的最簡便方法就是使用諸如Struts Console的工具,它自動格式化你的設定檔,以便與DTD保持一致。
配置validator-rules.xml
Validator架構可以設定為可插入系統,其驗證例行程式僅僅是插入到該系統中執行具體驗證的Java方法。validator-rules.xml檔案說明性地插入Validator用於執行驗證的驗證例行程式中。Struts應用程式範例帶有這個檔案的預配置拷貝。在大多數情況下,你不必修改這個預配置拷貝,除非你要向該架構中添加自己定製的驗證。
清單1 是一個樣本validator-rules.xml檔案,說明如何將驗證例行程式插入到Validator中。validator-rules.xml檔案中的每個驗證例行程式都有自己的定義,它用validator標記聲明,利用name屬性為該驗證例行程式指定邏輯名,並指定該例行程式的類和方法。該例行程式的邏輯名稱供該檔案中的其他例行程式以及validation.xml檔案中的驗證定義用於引用該例行程式。
請注意,validator標記放在javascript的標記中,javascript標記用於定義用戶端JavaScript代碼,以便在用戶端執行與伺服器端相同的驗證。
提供的驗證程式
預設情況下,Validator中包括幾個基本驗證例行程式,你可以用它們來處理大多數驗證問題。這些例行程式具有邏輯名稱,如required(用於輸入要求的值)、CreditCard(用於輸入信用卡號碼值)、email(用於輸入電子郵件地址值),等等。
建立Form Bean
為了使用Validator,你的應用程式的Form Bean必須歸到Validator的ActionForm的某一子類,而不是ActionForm本身。Validator的ActionForm子類提供了ActionForm的validate( )方法(它嵌入到Validator架構中)的實施過程。你不必從頭編寫驗證代碼並把它投入validate( )方法中,相反,可以完全忽略該方法,因為Validator為你提供了驗證代碼。
與Struts提供的核心功能相類似,Validator提供給你兩種可供選擇的方法來建立Form Bean。 你可以選擇的第一種方法就是像下面這樣建立一個特定的Form Bean對象:
package com.jamesholmes.minihr;import org.apache.struts.validator.ValidatorForm;public class LogonForm extends ValidatorForm { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; }public void setPassword(String password) { this.password = password; }}
這個類與你不是用Validator所建立的類相似,但它提供ValidatorForm而不是ActionForm。這個類也不提供ActionForm的空reset( )和validate( )方法的實施過程,因為ValidatorForm提供了相應過程。
在struts-config.xml檔案中配置這個特定Form Bean的方法與配置正則Form Bean的方法相同:
<form-beans> <form-bean name="logonForm" type="com.jamesholmes .minihr.LogonForm"/></form-beans>
用表單標記的name屬性給特定Form Bean指定的邏輯名是在定義validation.xml檔案中的驗證時所使用的名稱,如下所示:
<!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation// DTD Commons Validator Rules Configuration 1.0//EN" "http://jakarta.apache.org/ commons/dtds/validator_1_0.dtd"><form-validation> <formset> <form name="logonForm"> <field property="username" depends="required"> <arg0 key="prompt.username"/> </field> </form> </formset></form-validation>
Validator使用該表單標記的name屬性的值將驗證定義與要應用這些定義的Form Bean的名稱相匹配。
建立Form Bean時可以選擇的第二種方法是在struts-config.xml檔案中定義一個動態Form Bean,如下所示:
<form-beans> <form-bean name="logonForm" type="org.apache.struts.validator.DynaValidatorForm"> <form-property name="username" type="java.lang.String"/> <form-property name="password" type="java.lang.String"/> </form-bean></form-beans>
動態Form Bean不要求建立特定的Form Bean對象;相反,要定義Form Bean應該具有的屬性和類型,而Struts為你動態建立Form Bean。 Validator允許你使用這個概念,就像在核心Struts中使用這個概念一樣。與使用Validator的惟一區別就是要指定Form Bean是org.apache.struts.validator.DynaValidatorForm類型,而不是org.apache.struts.action.DynaActionForm類型。
分配給動態Form Bean的邏輯名是在定義validation.xml檔案中的驗證時使用的名稱。Validator使用與之相匹配的名稱將這些驗證與Form Bean聯絡在一起。
除了建立Form Bean的這兩種標準方法之外,Validator還提供了一個進階特性,用於將多個驗證定義與一個Form Bean定義聯絡起來。當你使用基於validatorForm或基於DynaValidatorForm的Form Bean時,Validator使用struts-config.xml檔案中的Form Bean的邏輯名稱,將Form Bean映射到validation.xml檔案中的驗證定義。這種機制在大多數情況下非常有用,但在某些時候,Form Bean要在多個操作中共用。 一個操作可能使用Form Bean的所有域(fields),而另一個操作可能只使用這些域的一個子集。因為驗證定義被串連到Form Bean,所以只使用域的一個子集的操作就無法繞過對未使用域的驗證。當驗證Form Bean時,就會對未使用的域建置錯誤訊息,因為Validator無從知道不去驗證未使用的域,它只是簡單地把它們看作缺失或無效。
為瞭解決這個問題,Validator提供了兩個附加的ActionForm子類,它使你能夠將驗證與操作相關聯,而不是與Form Bean相關聯。這樣你就可以根據哪個操作正在使用Form Bean來指定把哪些驗證用於該Form Bean了。對於特定的Form Bean,你要像下面這樣聲明org.apache.struts.validator.ValidatorActionForm子類:
public class AddressForm extends ValidatorActionForm { ...}
對於動態Form Bean,在struts-config.xml檔案中為Form Bean定義指定org.apache.struts.validator.DynaValidatorActionForm的類型,如下所示:
<form-bean name="addressForm" type="org.apache.struts.validator.DynaValidatorActionForm"> ...</form-bean>
在validation.xml檔案中,把一組驗證映射到一個操作路徑,而不是映射到Form Bean名,因為如果你定義了Create Address和Edit Address兩個操作(它們使用同一個Form Bean),那麼每個操作都會有一個惟一的操作名,如下所示:
<action-mappings> <action path="/createAddress" type="com.jamesholmes .minihr.CreateAddressAction" name="addressForm"/> <action path="/editAddress" type="com.jamesholmes .minihr.EditAddressAction" name="addressForm"/></action-mappings>
下面的validation.xml檔案片斷顯示了兩組驗證,它們用於同一個Form Bean,但卻有不同的操作路徑:
<formset> <form name="/createAddress"> <field property="city" depends="required"> <arg0 key="prompt.city"/> </field> </form> <form name="/editAddress"> <field property="state" depends="required"> <arg0 key="prompt.state"/> </field> </form></formset>
因為Form Bean要麼屬於ValidatorActionForm子類,要麼屬於DynaValidatorActionForm子類,所以Validator知道用一個操作路徑代替Form Bean的邏輯名稱來找出用於Form Bean的驗證。
配置validation.xml檔案
validation.xml檔案用於聲明將應用到Form Beans的一組驗證。要驗證的每個Form Bean在這個檔案中都有自己的定義。在這個定義中,指定要應用到該Form Bean的各域的驗證。下面是一個validation.xml檔案的例子,說明如何定義驗證:
<!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation// DTD Commons Validator Rules Configuration 1.0//EN" "http://jakarta.apache.org/ commons/dtds/validator_1_0.dtd"><form-validation> <formset> <form name="logonForm"> <field property="username" depends="required"> <arg0 key="prompt.username"/> </field> <field property="password" depends="required"> <arg0 key="prompt.password"/> </field> </form> </formset></form-validation>
validation.xml檔案的第一個元素是form-validation。這個元素是該檔案的主要元素,而且只定義一次。在form-validation元素內定義form-set元素,它包括多個表單元素。一般來說,在檔案中只定義一個form-set元素,但是如果要將驗證國際化,那就要在每個地方單獨使用一個form-set元素。
每個表單元素使用name屬性將名稱與其所包含的域驗證集關聯起來。Validator使用這個邏輯名稱將這些驗證映射到在struts-config.xml檔案中定義的一個Form Bean。根據要驗證的Form Bean的類型,Validator力求將該名稱與Form Bean的邏輯名稱或操作路徑相匹配。在表單元素內,field元素定義要應用到Form Bean的特定域的驗證。field元素的property屬性對應於特定Form Bean中的網域名稱。depends屬性利用validator-rules.xml檔案指定驗證例行程式的邏輯名稱,這些例行程式將應用到域驗證中。
配置ApplicationResources.properties
Validator使用Struts的資源綁定(Resource Bundle)機制將錯誤訊息具體化。不用在架構中對錯誤訊息進行寫入程式碼,Validator使你能在ApplicationResources.properties檔案中為一個訊息指定一個索引值,如果驗證失敗則將返回該索引值。validator-rules.xml檔案中的每個驗證例行程式都用validator標記的msg屬性指定錯誤訊息的索引值,如下所示:
<validator name="required" classname="org.apache.struts.validator.FieldChecks" method="validateRequired" methodParams="java.lang.Object, org.apache.commons.validator.ValidatorAction, org.apache.commons.validator.Field, org.apache.struts.action.ActionErrors, javax.servlet.http.HttpServletRequest" msg="errors.required">
如果在驗證例行程式運行時驗證失敗,則返回與msg屬性指定的索引值對應的訊息。
下面的片段顯示來自ApplicationResources.properties檔案的驗證出錯時的預設訊息集,它們由Struts應用程式範例提供。每個訊息的索引值對應於每個由validator-rules.xml檔案中的驗證例行程式所指定的訊息,它們由Struts應用程式範例提供。
# Error messages for Validator framework validationserrors.required={0} is required.errors.minlength={0} cannot be less than {1} characters.errors.maxlength={0} cannot be greater than {2} characters.errors.invalid={0} is invalid.errors.byte={0} must be a byte.errors.short={0} must be a short.errors.integer={0} must be an integer.errors.long={0} must be a long.0. errors.float={0} must be a float.errors.double={0} must be a double.errors.date={0} is not a date.errors.range={0} is not in the range {1} through {2}.errors.creditcard={0} is not a valid credit card number.errors.email={0} is an invalid e-mail address.
請注意,每條訊息都有預留位置,形式為{0}、{1}或{2}。在運行期間,預留位置被另一個值代替,如所驗證的域的名稱。這一特性特別有用,它使你能夠建立可被幾個不同的域重複使用的通用驗證錯誤訊息。
例如,下面給出required驗證的錯誤訊息errors.required:
errors.required={0} is required.
當你使用validation.xml檔案中的該required驗證時,必須定義用於替換該錯誤訊息中的{0}的值,如下所示:
<form name="auctionForm"> <field property="bid" depends="required"> <arg0 key="prompt.bid"/> </field></form>
錯誤訊息最多可以有4個預留位置:{0}和{3}。這些預留位置分別稱為arg0到arg3,你可以通過使用arg0~arg3標記來指定它們。在上面的例子中,arg0標記指定了用於替換{0}預留位置的值。該標記的key屬性指定來自ApplicationResources.properties檔案的一個訊息索引值,它的值用於替換預留位置,如下所示:
prompt.bid=Auction Bid
使用訊息索引值代替預留位置的值,這一方法使你不必在validation.xml檔案中對替換值反覆寫入程式碼。但是,如果你不想使用Resource Bundle的索引值/值機制來指定預留位置的值,則可以使用arg0標記的如下文法顯式地指定預留位置的值:
<arg0 key="Auction Bid" resource="false"/>
在這個例子中,resource屬性的值設為false,以便通知Validator要把該key屬性指定的值作為預留位置的值,而不要作為ApplicationResources.properties檔案中訊息的一個索引值。
啟用用戶端驗證
Validator除了提供了簡化伺服器端表單資料驗證過程的架構外,它還提供了執行用戶端驗證時便於使用的方法。在validator-rules.xml檔案中定義的每一個驗證例行程式都可以隨意指定JavaScript代碼,這些代碼可以在瀏覽器(用戶端上的)中運行,從而執行與伺服器端進行的驗證相同的驗證過程。在用戶端進行驗證時,除非所有表單都通過驗證,否則這些表單不允許被提交。
為了啟用用戶端驗證,必須在每個需要驗證的JSP中放上Struts HTML Tag Library(標記庫)的javascript標記,如下所示:
<html:javascript formName="logonForm"/>
javascript標記要求使用formName屬性來為想要對其執行驗證的表單指定validation.xml檔案中給出的表單定義名,如下所示:
<form name="logonForm"> <field property="username" depends="required"> <arg0 key="prompt.username"/> </field> <field property="password" depends="required"> <arg0 key="prompt.password"/> </field></form>
為表單定義指定的伺服器端的所有驗證都將在用戶端運行。由於用戶端驗證用JavaScript執行,所以可以有多種方法不去執行它。要確保驗證過程總是能運行,不論你是否選擇啟用了用戶端驗證,Validator都在伺服器端執行這些驗證。
結論
Validator架構針對錶單資料的驗證提供了可配置的系統,從而為核心Struts架構添加了很多有價值的功能。通過把Validator架構用於你的應用程式,你可以節約時間並簡化Struts應用程式的開發過程。