Struts Validator驗證器使用指南
驗證器:
從0.5版,驗證器在一些form中就已經(jīng)實現(xiàn)了,他最初包含在開發(fā)人員包中,后來核心代碼挪到Jakarta Commons包中和Struts特別擴展中作為 Struts 1.1的一部分。許多開發(fā)者為方便一直使用struts驗證器,這篇文檔首先概述驗證器的核心功能性,然后大概介紹在 struts1.1中的變化和新增功能。
如果你配置好驗證器插件,你應(yīng)該擴展ValidatorForm而不是ActionForm,以便它能加載你的Validator資源。他根據(jù)struts-config.xml文件中的action的name屬性為當(dāng)前form的調(diào)用相應(yīng)的驗證器,因此在validator-rules.xml中的form元素的名稱屬性應(yīng)該與action的name屬性值相匹配。
另外一種選擇是擴展ValidatorActionForm 而不是ValidatorForm,ValidatorActionForm使用struts-config.xml中action的path屬性,所以path屬性的值相應(yīng)的應(yīng)該與validator-rules.xml中的Form的name屬性匹配。
一個分離的action可以定義給多頁form的每個頁面,而且驗證規(guī)則可以與action關(guān)聯(lián)而不是與頁碼,就像驗證范例中的多頁form范例那樣。
國際化
在validator-rules.xml 文件中form的驗證規(guī)則可以組織為FormSet。FormSet 有與java.util.Locale 類相應(yīng)的屬性:如語言, 國家以及變量型屬性,如果他們未定義,F(xiàn)ormSet 將把它設(shè)置為默認(rèn)值。一個FormSet 也可以有關(guān)聯(lián)的常量。另外還可以定義與FormSet 同一級別的全局global元素,他與FormSet同樣也有常量。
注意:你必須在國際化的FormSet前聲明一個沒有國際化的默認(rèn)FormSet。這樣如果Validator沒有找到locale時可以有一個默認(rèn)版本。
可插入驗證器的默認(rèn)錯誤信息值可以被msg元素覆蓋。所以為mask驗證器生成錯誤信息的替代方法就是使用msg屬性,如果字段的name屬性與驗證器的name屬性匹配,那末將使用字段的msg屬性。
error messages的可以設(shè)置arg0-arg3 等參數(shù)元素。如果沒有設(shè)置arg0-arg3的name屬性, error messages將使用他們作為默認(rèn)的構(gòu)建參數(shù)值。如果設(shè)置了name屬性,你就可以把參數(shù)指定給一特定的可插入驗證器,然后這些參數(shù)將在構(gòu)造錯誤信息時被使用。
<field
property="lastName"
depends="required,mask">
<msg
name="mask"
key="registrationForm.lastname.maskmsg"/>
<arg0 key="registrationForm.lastname.displayname"/>
<var>
<var-name>mask</var-name>
<var-value>^[a-zA-Z]*$</var-value>
</var>
</field>
默認(rèn)的arg0-arg3元素將在消息資源中查找相應(yīng)的key,如果資源屬性設(shè)為false,她將把值直接傳進(jìn)去,而不從消息資源中查找。注意1.1版本中,你必須為每個模塊中明確地定義在驗證中用到的消息資源,否則將使用top-level資源。
<field
property="integer"
depends="required,integer,intRange">
<arg0 key="typeForm.integer.displayname"/>
<arg1
name="range"
key="${var:min}"
resource="false"/>
<arg2
name="range"
key="${var:max}"
resource="false"/>
<var>
<var-name>min</var-name>
<var-value>10</var-value>
</var>
<var>
<var-name>max</var-name>
<var-value>20</var-value>
</var>
</field>
常量/變量
全局的常量可以在全局標(biāo)簽中定義,F(xiàn)ormSet/本地常量能在formset 標(biāo)簽中創(chuàng)建。常量當(dāng)前僅僅是代替字段的property屬性,字段的var 元素的 value屬性,字段的msg 元素的 key屬性,字段的arg0-arg3 元素的 key屬性。字段的變量也可以在arg0-arg3 元素中被代替(例如:${var:min}))。替換的順序是FormSet/Locale常量第一,全局的常量第二,
arg elements 變量最后。
<global>
<constant>
<constant-name>zip</constant-name>
<constant-value>^\d{5}(-\d{4})?$</constant-value>
</constant>
</global>
<field
property="zip"
depends="required,mask">
<arg0 key="registrationForm.zippostal.displayname"/>
<var>
<var-name>mask</var-name>
<var-value>${zip}</var-value>
</var>
</field>
驗證器可以使用字段下面的變量部分來存儲變量,這些變量通過字段的getVar((String key)方法取得。
<field
property="integer"
depends="required,integer,intRange">
<arg0 key="typeForm.integer.displayname"/>
<arg1
name="range"
key="${var:min}" resource="false"/>
<arg2
name="range"
key="${var:max}" resource="false"/>
<var>
<var-name>min</var-name>
<var-value>10</var-value>
</var>
<var>
<var-name>max</var-name>
<var-value>20</var-value>
</var>
</field>
使用validwhen設(shè)計復(fù)雜的驗證
使用validwhen來設(shè)計復(fù)雜驗證的一個經(jīng)常的要求就是根據(jù)一個字段驗證另外一個字段(比如, 如果你要用戶兩次輸入口令來確認(rèn)值口令一致),另外一個就是表單中的一個字段只有另外一個字段有確定值的時候才是必須輸入的。新的validwhen驗證規(guī)則將很快被包含在1.1后的STRUTS版本中,她就是用來處理這種情況的。
validwhen 規(guī)則處理單個的變量字段,叫測試。這變量的值是一個布爾的表達(dá)式,如果驗證有效則它必須為真。可以包含這種變量的表達(dá)式有:
u 單引號或雙引號字符串literals,
u 十進(jìn)制、十六進(jìn)制、八進(jìn)制的Integer literals,
u null與null和空字符串匹配,
u 其它可以用屬性名引用的form字段,例如customerAge,
u 可以在外部因用得索引字段, 例如childLastName[2],
u 可以默認(rèn)implicit因用得索引字段, 例如childLastName[], 她將作為被索引的字段使用同樣的索引到數(shù)組中,
The literal *這里指它包含當(dāng)前測試字段的值,
作為例子,考慮一個包含通訊地址和郵箱字段的form。如果通訊地址不為空則郵箱字段是必須的required。你能這樣定義validwhen 規(guī)則:
<field property="emailAddress" depends="validwhen">
<arg0 key="userinfo.emailAddress.label"/>
<var>
<var-name>test</var-name>
<var-value>((sendNewsletter == null) or (*this* != null))</var-value>
</var>
</field>
上面定義的意思是:如果通訊地址是空或不空時這個字段時有效的。
這里有個稍微復(fù)雜的例子,它使用了索引字段。假定有一個表單,允許用戶輸入他們希望定購的部件號和數(shù)量。類orderLine 的bean的一數(shù)組被用來在稱為orderLines 的一屬性保持輸入項。
If you wished to verify that every line with part number also had a quantity entered, you could do it with:
如果你希望校驗訂單中有數(shù)量輸入得每一行,你可以這樣:
<field
property="quantity"
indexedListProperty="orderLines"
depends="validwhen">
<arg0 key="orderform.quantity.label"/>
<var>
<var-name>test</var-name>
<var-value>((orderLines[].partNumber == null) or (*this* != null))</var-value>
</var>
</field>
這里的意思是:如果相應(yīng)的partNumber 字段是空, 或這字段是不空的,則這字段是有效的。
最后一個例子,想象一表單,用戶必須輸入他們的以英寸為單位的高度,如果他們在高度在60英寸以下,則出一錯誤。(it is an error to have checked off nbaPointGuard as a career.)
<field property="nbaPointGuard" depends="validwhen">
<arg0 key="careers.nbaPointGuard.label"/>
<var>
<var-name>test</var-name>
<var-value>((heightInInches >= 60) or (*this* == null))</var-value>
</var>
</field>
給程序員的簡單說明:
所有的比較關(guān)系必須在parens 封裝。All comparisons must be enclosed in parens.
只有兩個itme時可以and或or鏈接。
如果比較的兩item都可以轉(zhuǎn)為整數(shù),則使用numeric比較,否則使用字符串比較。
可插入驗證器
驗證是從validation.xml 文件中加載的,默認(rèn)的驗證規(guī)則定義在validation.xml 文件中,默認(rèn)定義了required, mask ,byte, short, int, long, float, double, date (沒有本地支持), and a numeric range。
" mask "方式依賴于默認(rèn)值安裝要求,那意味著"required "可以完成,在"'mask "將運行以前"required "和" mask "方式被默認(rèn)包含進(jìn)框架中了。任何字段如果不是"required "而且是空或有零長度將跳過其他驗證。
如果使用了Javascript 標(biāo)簽,客戶端javascript在validator's javascript 屬性中查找值而且產(chǎn)生一個有驗證form方法的對象,要得到更多的關(guān)于Javascript Validator 標(biāo)簽工作細(xì)節(jié)的詳細(xì)的解釋,參閱html標(biāo)簽API參考。
"'mask' "方式讓你用一正則表達(dá)式掩碼驗證字段,它使用jakarta的正規(guī)表達(dá)式包,所有的有效性規(guī)則存儲在validator-rules.xml 文件,使用的主類是org.apache.regexp.RE。
validation.xml文件中的驗證器配置范例:
<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">
<validator name="mask"
classname="org.apache.struts.validator.FieldChecks"
method="validateMask"
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.invalid">
定義可插入驗證器
方法的參數(shù)是用逗號分隔的一些類名稱列表,方法屬性需要有一個符合上面的列表的簽名。列表由以下組合而成:
java.lang.Object – 要驗證的Bean。
org.apache.commons.validator.ValidatorAction – 當(dāng)前ValidatorAction。
org.apache.commons.validator.Field – 要驗證的字段
org.apache.struts.action.ActionErrors – 如果驗證錯誤將加入ActionError的錯誤對象javax.servlet.http.HttpServletRequest –當(dāng)前request 對象。
javax.servlet.ServletContext – 應(yīng)用的ServletContext。
org.apache.commons.validator.Validator–當(dāng)前的org.apache.commons.validator.Validator實例。
java.util.Locale – 當(dāng)前用戶的Locale。
多頁面form
字段部分有一可選的頁面屬性,它可以被設(shè)為整數(shù),頁上字段的所有驗證小于或等于服務(wù)器端驗證的當(dāng)前頁,頁上字段的所有驗證小于或等于客戶端頁上所有字段的驗證小于或等于服務(wù)器端驗證的當(dāng)前頁驗證的當(dāng)前頁。一個mutli-part表單需要定義頁面屬性:
<html:hidden property="page" value="1"/>。
比較兩個字段
這是一個展示你怎樣才能比較兩個字段是否有一樣的值的例子。比如“用戶改變他們的口令“一般會有口令字段和一確認(rèn)字段。
<validator name="twofields"
classname="com.mysite.StrutsValidator"
method="validateTwoFields"
msg="errors.twofields"/>
<field property="password" depends="required,twofields">
<arg0 key="typeForm.password.displayname"/>
<var>
<var-name>secondProperty</var-name>
<var-value>password2</var-value>
</var>
</field>
public static boolean validateTwoFields(
Object bean, ValidatorAction va,
Field field, ActionErrors errors, HttpServletRequest request,
ServletContext application) {
String value = ValidatorUtils.getValueAsString( bean, field.getProperty());
String sProperty2 = field.getVarValue("secondProperty");
String value2 = ValidatorUtils.getValueAsString( bean, sProperty2);
if (!GenericValidator.isBlankOrNull(value)) {
try {
if (!value.equals(value2)) {
errors.add(field.getKey(),
Resources.getActionError( application, request, va, field));
return false;
}
} catch (Exception e) {
errors.add(field.getKey(), Resources.getActionError( application, request, va, field));
return false;
}
}
}
已知的bug
Struts Validator依賴于Commons Validator包,所以問題報告和增強需求可能在兩個產(chǎn)品中列出。
· Struts Validator Bugzilla Reports
· Commons Validator Bugzilla Reports
變更和deprecations
新建的標(biāo)記屬性。
<html:javascript>標(biāo)記有新的屬性定義.
使用commons-validator.jar中的DTD驗證。
當(dāng)前使用的驗證XML文件是根據(jù)commons-validator.jar中的DTD。Struts不在為validator-rules.xml and validator.xml.單獨維護(hù)一個分離的DTD,另外,commons-validator 現(xiàn)在維護(hù)一個統(tǒng)一的validator.dtd。修改所有validator.xml文件的DTD引用為
<!DOCTYPE form-validation PUBLIC
"-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN"
"
空字段。
當(dāng)前默認(rèn)在所有得基礎(chǔ)驗證類型中忽略空白的字段,如果你要求一個字段必須輸入,那末在你的應(yīng)用的validator.xml 文件相應(yīng)的字段定義的depends屬性中添加 " required "。
新建的范圍RANGE方法.
JavaScript 和JAVA中都添加了intRange & floatRange 方法。
有條件地REQUIRED字段.
最大的修改是添加了基于其她字段的值的有條件地require驗證的能力。它允許你定義邏輯如:“只有X字段非空的時候Y字段為’male’才有效”,這是實現(xiàn)上述邏輯的推薦方法,這種方法在1.1版后的第一版將實現(xiàn)。在1.1版中添加的Requiredif驗證規(guī)則,將在新版中去掉。不過,如果你正準(zhǔn)備使用requiredif,這里有一個簡短的教程。
讓我們假定你有一個有3個字段的醫(yī)藥的信息表單,性別sex,懷孕測試pregnancyTest,測試結(jié)果testResult,如果性別為'f' or 'F',則懷孕測試pregnancyTest是required,如果pregnancyTest不是空,測試結(jié)果testResult是required。
你的validation.xml 文件的輸入項應(yīng)該是這樣的:
<form name="medicalStatusForm">
<field property="pregnancyTest" depends="requiredif">
<arg0 key="medicalStatusForm.pregnancyTest.label"/>
<var>
<var-name>field[0]</var-name>
<var-value>sex</var-value>
</var>
<var>
<var-name>fieldTest[0]</var-name>
<var-value>EQUAL</var-value>
</var>
<var>
<var-name>fieldValue[0]</var-name>
<var-value>F</var-value>
</var>
<var>
<var-name>field[1]</var-name>
<var-value>sex</var-value>
</var>
<var>
<var-name>fieldTest[1]</var-name>
<var-value>EQUAL</var-value>
</var>
<var>
<var-name>fieldValue[1]</var-name>
<var-value>f</var-value>
</var>
<var>
<var-name>fieldJoin</var-name>
<var-value>OR</var-value>
</var>
</field>
<field property="testResult" depends="requiredif">
<arg0 key="medicalStatusForm.testResult.label"/>
<var>
<var-name>field[0]</var-name>
<var-value>pregnancyTest</var-value>
</var>
<var>
<var-name>fieldTest[0]</var-name>
<var-value>NOTNULL</var-value>
</var>
</field>
</form>
這里有一個使用索引的屬性更復(fù)雜的例子,如果你的struts-config.xml 有這下面:
<form-bean name="dependentlistForm"
type="org.apache.struts.webapp.validator.forms.ValidatorForm">
<form-property
name="dependents"
type="org.apache.struts.webapp.validator.Dependent[]" size="10"/>
<form-property name="insureDependents" type="java.lang.Boolean" initial="false"/>
</form-bean>
這里dependentlistForm bean有l(wèi)astName,firstName,dob,coverageType四個屬性,你可以這樣定義一驗證規(guī)則:
<form name="dependentlistForm">
<field
property="firstName" indexedListProperty="dependents" depends="requiredif">
<arg0 key="dependentlistForm.firstName.label"/>
<var>
<var-name>field[0]</var-name>
<var-value>lastName</var-value>
</var>
<var>
<var-name>fieldIndexed[0]</var-name>
<var-value>true</var-value>
</var>
<var>
<var-name>fieldTest[0]</var-name>
<var-value>NOTNULL</var-value>
</var>
</field>
<field
property="dob" indexedListProperty="dependents" depends="requiredif,date">
<arg0 key="dependentlistForm.dob.label"/>
<var>
<var-name>field[0]</var-name>
<var-value>lastName</var-value>
</var>
<var>
<var-name>fieldIndexed[0]</var-name>
<var-value>true</var-value>
</var>
<var>
<var-name>fieldTest[0]</var-name>
<var-value>NOTNULL</var-value>
</var>
</field>
<field
property="coverageType" indexedListProperty="dependents" depends="requiredif">
<arg0 key="dependentlistForm.coverageType.label"/>
<var>
<var-name>field[0]</var-name>
<var-value>lastName</var-value>
</var>
<var>
<var-name>fieldIndexed[0]</var-name>
<var-value>true</var-value>
</var>
<var>
<var-name>fieldTest[0]</var-name>
<var-value>NOTNULL</var-value>
</var>
<var>
<var-name>field[1]</var-name>
<var-value>insureDependents</var-value>
</var>
<var>
<var-name>fieldTest[1]</var-name>
<var-value>EQUAL</var-value>
</var>
<var>
<var-name>fieldValue[1]</var-name>
<var-value>true</var-value>
</var>
<var>
<var-name>fieldJoin</var-name>
<var-value>AND</var-value>
</var>
</field>
</form>
這里的意思是:
如果lastName 字段是非空的,firstName 字段required。因為字段Indexed 為真,這它意味著lastName的indexed 必須與firstName 的索引的一樣,dob同理,除非date不為空。
如果lastName 用樣索引時的值不空, 而且非索引字段insureDependents為真,則coverageType 是only require。
你可以對字段在[n]中使用任意數(shù)字,唯一的限制是他們必須都是AND或OR,你無法混合使用。
Deprecation:
u JavaScript 和Java的range方法.
u StrutsValidator &StrutsValidatorUtil 類中的Deprecation方法
驗證器api指南
一個簡明的Struts驗證器API指南 可以幫助你開始。
驗證器資源
Struts Validator: Validating Two Fields Match 作者M(jìn)att Raible。(兩個字段匹配驗證)關(guān)于使用方法的文章。(范例部分為翻譯此文內(nèi)容)
DynaForms and the Validator 作者James Turner and Kevin Bedell。Struts Kickstart的其中一章(動態(tài)form和驗證器),可以自由下載PDF).
Validating user input 作者 David Winterfeldt and Ted Husted。Struts in Action的其中一章,可以自由下載(PDF)。