前面講到了一種驗(yàn)證方法,是在Action的 validate方法中通過代碼的方式來完成的。而struts2提供了另外一種 方式來實(shí)現(xiàn)輸入驗(yàn)證。
這種方式就是使用validate框架來實(shí)現(xiàn)輸入校驗(yàn),這種方式是基于XML的驗(yàn)證。
文件名為XXXAction-validation.xml。
那么校驗(yàn)xml文件格式該如何寫呢?
可以使用firefox查看此xml的DTD定義,地址為 http://www./xwork/xwork-validator-1.0.2.dtd
在此列出此DTD的內(nèi)容
<?xml version="1.0" encoding="UTF-8"?>
<!--
XWork Validators DTD.
Used the following DOCTYPE.
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.2//EN"
"http://www./xwork/xwork-validator-1.0.2.dtd">
-->
<!ELEMENT validators (field|validator)+>
<!ELEMENT field (field-validator+)>
<!ATTLIST field
name CDATA #REQUIRED
>
<!ELEMENT field-validator (param*, message)>
<!ATTLIST field-validator
type CDATA #REQUIRED
short-circuit (true|false) "false"
>
<!ELEMENT validator (param*, message)>
<!ATTLIST validator
type CDATA #REQUIRED
short-circuit (true|false) "false"
>
<!ELEMENT param (#PCDATA)>
<!ATTLIST param
name CDATA #REQUIRED
>
<!ELEMENT message (#PCDATA)>
<!ATTLIST message
key CDATA #IMPLIED
>
由此DTD的定義可知,此XML文件的根元素為validators。
在根元素下可以有若干個(gè)field或validator子元素,即分別代表著字段校驗(yàn)和非字段校驗(yàn),它們的區(qū)別將在后面介紹。
字段校驗(yàn)
字段校驗(yàn)代表著field,標(biāo)簽有個(gè)name屬性石必填的,它和表單中的name屬性值是一樣的。
這里我填入username。
<validators>
<field name="username">
</field>
</validators>
field下面有個(gè)子元素叫field-validator,代表著要用什么方式來進(jìn)行校驗(yàn),其有個(gè)屬性叫 type,也是必填的。
<validators>
<field name="username">
<field-validator type="">
</field-validator>
</field>
</validators>
type應(yīng)該填入什么內(nèi)容呢?
可以查看xwork的源文件,在包 com.opensymphony.xwork2.validator.validators下有個(gè)文件default.xml,在這個(gè)文件中定義了 type屬性值。
<validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
<validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>
還有很多定義好的type值,這里不一一列舉
在validator元素中,name屬性表示可以定義的type值,class 表示用哪個(gè)類來進(jìn)行校驗(yàn),這是struts2默認(rèn)設(shè)置好的校驗(yàn)器,我們可以直接使用。
在這個(gè)例子中,我們將type設(shè)置為requiredstring
<validators>
<field name="username">
<field-validator type="requiredstring">
</field-validator>
</field>
</validators>
在field-validator下面有兩個(gè)子元素,param和message
param可以任意個(gè),可有可無,但是message有且只有一個(gè)。
param表示傳入的參數(shù),message表示出錯(cuò)時(shí)顯示的信息。也就是說message可以是任意的字符串,但是param卻是特定的。
在繼續(xù)之前還是看看 com.opensymphony.xwork2.validator.validators.RequiredStringValidator這個(gè)類的源代碼。首先確定已經(jīng)下載了源代碼,并且在MyEclipse中關(guān)聯(lián)了源代碼,這是一個(gè)好習(xí)慣。
在這個(gè)類中有一個(gè)成員變量
private boolean doTrim = true;
因此,我們在param中要做的就是,將param元素的name屬性設(shè)置為doTrim,值為true。
<validators>
<field name="username">
<field-validator type="requiredstring">
<param name="trim"<true>/param>
<message<username should not be blank!</message>
</field-validator>
</field>
</validators>
trim表示是否忽略空格,默認(rèn)是true,因此在此也可以省略掉param元素。
其實(shí)在 field-validator元素中還有一個(gè)屬性short-circuit,其默認(rèn)值是false,表示的意思是短路,即前面驗(yàn)證失敗,后面就不做驗(yàn)證了。
然而這僅僅只是一個(gè)校驗(yàn)條件,還是以 username為例,在上次的例子中,還要求username的長度要在6到10之間,因此,繼續(xù)看default.xml文件
在其中找到這樣一個(gè)元素
<validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>
從字面上可以看出是限制字符串長度的,因此查看這個(gè)實(shí)現(xiàn)類
它有3個(gè)成員變量:
private boolean doTrim = true;
private int maxLength = -1;
private int minLength = -1;
其中最主要的是maxLength和minLength分別代表最大長度和最小長度
因此在這個(gè)校驗(yàn)器中內(nèi)容應(yīng)該如下:
<field name="username">
<field-validator type="stringlength">
<param name="minLength">6</param>
<param name="maxLength">10&lr;/param>
<message>username should be between ${minLength} and ${maxLength}</message>
</field-validator>
</field>
有個(gè)問題就是
<validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
<validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>
required和requiredstring有什么區(qū)別嗎?
requiredstring是指這個(gè)字符串是必須的,而required表示這個(gè)字段是必須的,而沒有指明這個(gè)字段是否必須是字符串。
因此對于字符串,可以使用requiredstring,而對于非字符串類型就必須使用required了。如在這個(gè)示例中的age,birthday。注意birthday是Date類型的,不是String。
還是回顧一下register2.jsp的頁面內(nèi)容。
body中的內(nèi)容為:
<s:actionerror />
<s:form action="register2" theme="simple">
……
……
</s:form>
但是輸入數(shù)據(jù)提交后,仍然顯示的是以前的出錯(cuò)信息,并不是今天所設(shè)置的信息。
這就涉及到一些知識(shí)點(diǎn)了,因?yàn)樾r?yàn)框架產(chǎn)生的錯(cuò)誤是fielderror,不會(huì)在actionerror中顯示。
因此,需要將 theme="simple"去掉,并把那些表格標(biāo)簽頁去掉,然后執(zhí)行時(shí)會(huì)看到在username上方的錯(cuò)誤信息。
還有一個(gè)問題就是validate校驗(yàn)框架和Action中validate方法是否沖突。
實(shí)際上雖然會(huì)使用框架驗(yàn)證,但是也會(huì)調(diào)用Action的validate方法,通過上面的顯示結(jié)果應(yīng)該可以清楚的看到,在表單上面集中的顯示了actionerror。
但是如果把Action中的validate方法的出錯(cuò)信息add到field中會(huì)有什么效果呢??
修改RegisterAction中validate方法,將 addActionError改為addFieldError。
當(dāng)沒有輸入用戶名時(shí),會(huì)顯示如下錯(cuò)誤信息:
username should not be blank!
username invalid
為什么會(huì)顯示這樣的結(jié)果呢???
首先肯定的是在validate中增加的fielderror,和xml中不會(huì)沖突
即錯(cuò)誤信息不會(huì)被覆蓋,而是兩者都有,而且先顯示xml中定義的錯(cuò)誤信息,然后才是validate中定義的錯(cuò)誤信息。實(shí)際上是所有的xml執(zhí)行完畢后,在執(zhí)行validate。
為什么會(huì)產(chǎn)生這樣的效果呢???那么你就必須知道fielderror到底是什么!繼續(xù)查看源代碼。
因?yàn)镽egisterAction是繼承的 ActionSupport,addFieldError是繼承自ActionSupport,所以先看看ActionSupport中 addFieldError的實(shí)現(xiàn)方法。
在 ActionSupport中addFieldError是這么實(shí)現(xiàn)的,
public void addActionError(String anErrorMessage) {
validationAware.addActionError(anErrorMessage);
}
即通過調(diào)用validationAware對象的addActionError,而validationAware是 ValidationAwareSupport的一個(gè)實(shí)例,在ValidationAwareSupport中定義了fielderror是什么。
private Map>String, List<String>> fieldErrors;
public synchronized void addFieldError(String fieldName, String errorMessage) {
final Map<String, List<String>> errors = internalGetFieldErrors();
List<String> thisFieldErrors = errors.get(fieldName);
if (thisFieldErrors == null) {
thisFieldErrors = new ArrayList<String>();
errors.put(fieldName, thisFieldErrors);
}
thisFieldErrors.add(errorMessage);
}
通過源代碼,可以看到fieldErrors實(shí)際上是一個(gè)Map>String, List<String>>。key是String類型的,而value是List<String>
而實(shí)際上這個(gè)List是通過ArrayList<String>來實(shí)現(xiàn)的,也就是說,key是String類型的,而value是ArrayList<String>。
雖然一個(gè)key只能對應(yīng)一個(gè)value,但是在這里value并不是一個(gè)字符串,而是一個(gè)數(shù)組。所以錯(cuò)誤信息不會(huì)被覆蓋掉。
在ActionSupport類中存在一個(gè)方法getFieldErrors,按照方法名可以猜的出該方法返回的是fieldError這個(gè)數(shù)組,既然如此那么是否可以通過 getFieldErrors直接添加呢??
List<String> list = new ArrayList<String>();
list.add("username should be between 6 and 10");
this.getFieldErrors().put("username",list);
雖然編譯器沒有報(bào)錯(cuò),但是實(shí)際上是不行的。查看API文檔:
getFieldErrors,其解釋如下:
public Map<String,List<String>> getFieldErrors()
Description copied from interface: ValidationAware
Get the field specific errors associated with this action. Error messages should not be added directly here, as implementations are free to return a new Collection or an
Unmodifiable Collection.
注釋:
這個(gè)方法返回與這個(gè)action相關(guān)的具體的fielderrors,錯(cuò)誤信息不能直接從這里添加,執(zhí)行結(jié)果返回一個(gè)新的集合或一個(gè)不可修改的集合
這是什么意思呢?看源代碼,這個(gè)方法同樣是在 ValidationAwareSupport中實(shí)現(xiàn)的。
public synchronized Map<String, List<String>> getFieldErrors() {
return new LinkedHashMap<String, List<String>>(internalGetFieldErrors());
}
由此可以看到該方法返回的是一個(gè)新的LinkedHashMap,只是一個(gè)拷貝,而不是原集合,所以這樣直接添加是無法顯示出來的。
那么何時(shí)使用validate驗(yàn)證框架,什么時(shí)候使用action中的 validate方法呢?
一般來說,簡單驗(yàn)證可以使用 xml,復(fù)雜時(shí)用validate
前面講到了 struts2的數(shù)據(jù)校驗(yàn),那么為什么要有服務(wù)器校驗(yàn)??擁有了客戶端校驗(yàn)不是也行嗎??
服務(wù)端校驗(yàn)是必須的,即使有客戶端校驗(yàn)。因?yàn)榭梢圆煌ㄟ^browser訪問web服務(wù)器?。?qiáng)健的web應(yīng)用要有客戶端和服務(wù)器端的驗(yàn)證。
當(dāng)然,struts2同樣支持客戶端驗(yàn)證。
要使用struts2的客戶端校驗(yàn),必須滿足一下條件:
1.form的主題(theme)一定不能設(shè)置為simple
2.將struts2 form標(biāo)簽中validate屬性設(shè)置為true
但是看到小時(shí)效果后就會(huì)發(fā)現(xiàn),struts2生成的也是js代碼,但是效果卻很差,所以一般來說,使用struts2的服務(wù)器端校驗(yàn),而客戶端校驗(yàn)自己寫。
struts2標(biāo)簽支持事件,可以像使用html標(biāo)簽一樣使用。
同樣用validate校驗(yàn)框架也應(yīng)該可以使用局部校驗(yàn):
當(dāng)action中有多個(gè)方法時(shí),需要在和action同目錄下新建文件 XXXAction-XXX(方法名)-validation.xml
在實(shí)例化子類對象時(shí),會(huì)先執(zhí)行父類的全局校驗(yàn),然后是局部校驗(yàn),接著是子類的全局校驗(yàn),然后是子類的局部校驗(yàn),因此不要提供全局校驗(yàn)。
字段校驗(yàn)和非字段校驗(yàn)的區(qū)別
通俗點(diǎn)講:
字段校驗(yàn):校驗(yàn)誰,用什么方法
非字段校驗(yàn):用什么校驗(yàn),校驗(yàn)誰
非字段校驗(yàn)示例:
<validator type="requiredstring">
<param name="fieldName">username</param>
<message></message>
</validator>
其中tyoe="fieldName"是不變的。至于其他細(xì)節(jié)不在這里詳細(xì)敘述。
但還是建議使用字段校驗(yàn)器。
通過今天的學(xué)習(xí),了解到了另外一種通過配置文件進(jìn)行數(shù)據(jù)校驗(yàn)的方法。而這種方法顯得更加簡單,易懂。
下次課將談到struts2的攔截器。