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