表現層數據是由用户輸入的,用户輸入則是非常複雜的,正常用户的偶然錯誤,還有Cracker(破壞者)的惡意輸入,都可能導致系統出現非正常情況。例如,在如圖4.2所示的輸入頁面中,我們希望用户輸入crazyit.org,leegang模式的字符串,希望用户輸入的字符串包含一個英文逗號(,)作為用户名和密碼的分隔符,如果用户輸入多於一個的英文逗號,或者沒有輸入英文逗號,都將引起系統異常——因為上面的類型轉換器將無法正常分解出用户名和密碼。
實際上,表現層數據涉及的兩個處理:數據校驗和類型轉換是緊密相關的,只有當輸入數據是有效數據時,系統才可以進行有效的類型轉換——當然,有時候即使用户輸入的數據能進行有效轉換,但依然是非法數據(假設需要輸入一個人的年齡,輸入200則肯定是非法數據)。因此,可以進行有效的類型轉換是基礎,只有當數據完成了有效的類型轉換後,下一步才去做數據校驗。
Struts 2提供了一個名為conversionError的攔截器,這個攔截器被註冊在默認的攔截器棧中。我們查看Struts 2框架的默認配置文件struts-default.xml,該文件中有如下配置片段:
<interceptor-stack name="defaultStack">
省略其他攔截器引用
處理類型轉換錯誤的攔截器
處理數據校驗的攔截器
省略其他攔截器
</interceptor-stack>
在上面的默認攔截器棧中包含了conversionError攔截器的引用,如果Struts 2的類型轉換器執行類型轉換時出現錯誤,該攔截器將負責將對應錯誤封裝成表單域錯誤(FieldError),並將這些錯誤信息放入ActionContext中。
顯然,conversionError攔截器實際上是AOP中的Throws處理(關於各種處理類型的定義和深入介紹,請參閲本書關於Spring的介紹)。Throws處理當系統拋出異常時啓動,負責處理異常。通過這種方式,Struts 2的類型轉換器中只完成類型轉換邏輯,而無須關心異常處理邏輯。因此,我們看到上面的類型轉換器無須進行任何異常處理邏輯。
圖4.6顯示了Struts 2類型轉換中的錯誤處理流程。
圖4.6只顯示了類型轉換器、conversionError攔截器和控制器之間的順序圖,並未完全刻畫出系統中的其他成員。當conversionError攔截器對轉換異常進行處理後,系統會跳轉到名為input的邏輯視圖。
圖4.6 Struts 2類型轉換中的錯誤處理流程
為了讓Struts 2框架處理類型轉換的錯誤,以及使用後面的數據校驗機制,系統的Action類都應該通過繼承ActionSupport類來實現。ActionSupport類為完成類型轉換錯誤處理,數據校驗實現了許多基礎工作。
1.處理類型轉換錯誤
下面將以最簡單的局部類型轉換器為例,介紹如何處理類型轉換錯誤。
我們重新改寫系統的Action類,讓系統的Action類繼承Struts 2的ActionSupport類。修改後的Action類代碼片段如下。
程序清單:codes\04\4.1\errorHandler\WEB-INF\src\org\crazyit\app\action\LoginAction.java
//為了正常使用系統的類型轉換錯誤處理機制,讓Action類繼承ActionSupport類
public class LoginAction
extends ActionSupport
{
該類包含一個User類型屬性,該屬性用於封裝名為user的請求參數
省略該類的其他成分
}
為了讓Struts 2類型轉換的錯誤處理機制生效,包括下一節的輸入校驗生效,都必須讓Action繼承Struts 2的ActionSupport基類,因為Struts 2的ActionSupport負責收集類型轉換錯誤、輸入校驗錯誤,並將它們封裝成FieldError對象,添加到ActionContext中。
前面已經提到,當類型轉換出現異常時,conversionError攔截器會處理該異常,然後轉入名為input的邏輯視圖,因此應該為該Action增加名為input的邏輯視圖定義。修改後的struts.xml文件代碼如下。
程序清單:
codes\04\4.1\errorHandler\WEB-INF\src\struts.xml
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE struts PUBLIC
<struts>
配置國際化資源文件
配置名為input的邏輯視圖,
當轉換失敗後轉入該邏輯視圖
</struts>
上面的粗體字代碼為input邏輯視圖指定了物理視圖資源:input.jsp。經過上面配置,如果用户輸入不能成功轉換成User對象,系統將轉入input.jsp頁面,等待用户再次輸入。
前面已經講述過,Struts 2會負責將轉換錯誤封裝成FieldError,並將其放在ActionContext中,這樣就可以在對應視圖中輸出轉換錯誤,在頁面中使用<s:fielderror/>標籤即可輸出類型轉換錯誤信息。
在默認情況下,使用<s:fielderror/>標籤會輸出形如Invalid field value for field xxx的錯誤提示信息,其中xxx是Action中屬性名,也是該屬性對應的請求參數的名。
對於中文環境而言,用户通常希望看到中文的提示信息,因此應該改變默認的提示信息。只需在應用的國際化資源文件中增加如下一行代碼,即可改變默認的類型轉換錯誤的提示信息。
#改變默認的類型轉換失敗後的提示信息
xwork.default.invalid.fieldvalue={0}字段類型轉換失敗!
因為上面的資源文件中包含了非西歐字符,因此必須使用native2ascii命令來處理該文件。
也就是説,Struts 2使用key為xwork.default.invalid.fieldvalue的消息作為標準的提示信息,並在input.jsp頁面中增加如下代碼:
<!-- 輸出類型轉換錯誤、輸入校驗提示
<s:fielderror/>
改變了默認提示信息後,如果再次提交包含不能合理轉換的請求參數,將看到如圖4.7所示的頁面。
圖4.7 類型轉換錯誤的提示信息
|
提示:
|
當我們使用 Struts 2 提供的表單標籤來生成表單時,這些表單標籤不僅可以為我們增加額外的佈局功能,還可以自動輸出類型轉換失敗的提示信息和輸入校驗失敗的提示信息。
在某些時候,可能還需要對特定字段指定特別的提示信息,此時可通過Action的局部資源文件來實現,在文件中增加如下一項:
invalid.fieldvalue.<propName>=<tipMsg>
將其中<propName>替換成需要進行類型轉換的屬性名(此處的propName可以支持OGNL表達式,例如user.birth代表Action裏user屬性的birth屬性),<tipMsg>替換成轉換失敗後的提示信息,上面的轉換錯誤提示就會發揮作用了。
對於如圖4.8所示的請求頁面,其中包含了用户姓名、用户年齡和用户生日三個表單域,它們代表三個請求參數,這三個請求參數由Struts 2採用字符串、整數型和日期類型屬性封裝,因此必須涉及到類型轉換!本應用的類型轉換是基於OGNL表達式的類型轉換。
圖4.8 輸入用户信息的輸入頁面
處理上面請求的Action類代碼如下。
程序清單:
codes\04\4.1\errorHandler2\WEB-INF\src\org\crazyit\app\action\LoginAction.java
public class LoginAction extends ActionSupport
{
private User user;
屬性的setter和getter方法
沒有execute方法,直接使用ActionSupport的execute方法
}
因為要改變birth屬性的類型轉換失敗的提示信息,所以我們為該Action提供一個局部資源文件,該文件內只包含如下一行代碼。
程序清單:codes\04\4.1\errorHandler2\WEB-INF\src\org\crazyit\app\action\LoginAction.properties
#改變上面的Action中user屬性的birth屬性類型轉換後的提示信息
invalid.fieldvalue.user.birth=生日信息必須滿足yyyy-MM-dd格式
該文件的文件名為LoginAction.properties(用native2ascii處理後新文件名為LoginAction_zh_CN. properties),將該文件放在與LoginAction.class相同的位置(例如WEB-INF\classes\org\crazyit\app\action路徑下)。如果我們在如圖4.8所示的輸入頁面中輸入了不能成功進行類型轉換的字符串,將看到如圖4.9所示的頁面。
在圖4.9中可以看到,輸入的年齡參數無法正常轉換,生日參數也無法正常轉換,其中“age字段無效”是全局的轉換錯誤提示,由xwork.default.invalid.fieldvalue消息提供,後面的生日字段的轉換錯誤提示則是單獨指定的。
|
|
上面的轉換錯誤信息是紅色的,而不是黑色的,
僅僅是因為筆者增加了一個<s:head/>標籤,該標籤可以導入xhtml主題所需要的一些CSS樣式。
圖4.9 輸出類型轉換的錯誤提示
2.處理集合屬性的轉換錯誤
如果Action裏包含一個集合屬性,只要Struts 2能檢測到集合裏元素的類型(可以通過局部類型轉換文件指定,也可通過泛型方式指定),類型轉換器就可以正常起作用。當類型轉換器在執行類型轉換過程中出現異常時,系統的conversionError攔截器就會處理該異常,處理結束後返回名為input的邏輯視圖。
假設有如下Action處理類,該處理類裏包含一個List集合屬性。
程序清單:codes\04\4.1\ListErrorHandler\WEB-INF\src\org\crazyit\app\action\LoginAction.java
//使用Struts 2的類型轉換的錯誤機制,應該繼承ActionSupport
public class LoginAction extends ActionSupport
{
屬性的setter和getter方法
對於上面的Action,該Action需要的users屬性是一個List集合,我們有兩種方式來傳入請求參數:
Ø 只傳入一個users請求參數,該請求參數的值是字符串數組的形式;
Ø 分別傳入多個users[0]、users[1]…形式的請求參數,這種形式將會充分利用OGNL表達式類型轉換機制。
對於第一種形式,因為只有一個請求參數,請求參數名為users,只要任何一個users請求參數不能成功轉換成User對象,Struts 2都會提示users字段無效,如圖4.10所示。
如果將三個請求參數的名字設為users[0]、users[1]…的形式,Struts 2將可以區分每個請求參數,從而顯示更友好的轉換錯誤提示。例如,我們將表單頁的代碼改為如下。
程序清單:codes\04\4.1\ListErrorHandler\ognlInput.jsp
<s:form action="login">
將會依次生成多個請求參數
圖4.10 集合屬性類型轉換失敗
label="第%{#stat.index}個用户信息"/>
轉換" theme="simple"/>
重填" theme="simple"/></td>
</s:form>
上面的頁面代碼我們使用了迭代器標籤來指定三個表單域的name,三個表單域的name將分別是users[0]、users[1]、users[2],在這種情況下如果任一個表單域類型轉換失敗,將看到如圖4.11所示的頁面。
圖4.11 集合屬性類型轉換失敗