頁面流驗證提供以下功能:
· 用戶表單輸入的服務端驗證
· 用待驗證的表單bean封裝驗證邏輯
· 驗證失敗時自動導航到輸入頁面
· 內置消息包支持,方便進行國際化
本文假定讀者熟悉頁面流及JSP。本文將介紹創建表單bean和驗證頁面的基本步驟。如果發生驗證錯誤用戶將會得到通知,從而可以修正它們;動作方法被調用時可以認爲數據是正確的。
Request生命週期及驗證
表單驗證針對的是已提交表單bean中的內容。通過JSP頁面的<netui:form> 標籤的動作屬性,該bean被間接選中;表單bean與指定動作相關聯。表單bean繼承自com.bea.wlw.netui.pageflow.FormData。爲了驗證表單bean,開發人員需要重載validate() 方法並提供驗證邏輯。
圖1演示了request的生命週期以及驗證是如何發生的。當Strut的控制器接收到請求時,它將請求傳遞給頁面流RequestProcessor。請求處理器的processPopulate 方法首先構造一個新的表單bean,其中含有來自請求的所有數據條目。此步驟將會創建FormData。然後請求處理器檢查是否爲動作激活了表單驗證。如果驗證被激活,將會針對該表單bean調用validate()方法。如果出現驗證錯誤,控制權將交給一個輸入頁面,同時轉交的還有用戶輸入的數據以及錯誤信息。如果沒有發現錯誤,控制權將被傳遞給目標動作從而繼續處理請求。
圖1中的藍色方框代表頁面流代碼,黑色方框代表用戶代碼。
圖1——請求驗證生命週期
通過簡單的五個步驟就可以在頁面流中進行驗證。下面詳細解釋每個步驟。
步驟1——驗證方法
驗證用戶輸入的第一個步驟是在表單bean上創建validate()方法。該方法將會檢查表單bean的屬性,察看有沒有用戶輸入錯誤。下面這個非常簡單的表單bean定義了兩個屬性:name 和type。validate() 方法提供了對這些屬性的驗證。
<?XML:NAMESPACE PREFIX = O /> public static class NameBean extends FormData { private String name; private String type; public void setType(String type) { this.type = type; } public String getType() { return this.type; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { ActionErrorserrors = new ActionErrors(); if (name == null || name.equals("")) { errors.add("nameError", new ActionError("NullNameError")); } else { if (!Character.isUpperCase(name.charAt(0))) { errors.add("nameError", new ActionError("UpperCaseNameError",name)); } } if (type == null || (!type.equals("bar") && !type.equals("foo"))) { errors.add("typeError", new ActionError("TypeError",type)); } if (!errors.empty()) { request.setAttribute("errorNotSet",new Boolean(false)); } return errors; } } |
validate() 方法返回Struts的ActionErrors 對象,其中包含任何可能發生的錯誤信息。當該方法被調用時,它接收Struts ActionMapping 結構和HttpServletRequest。如果方法內部發生錯誤,Struts ActionError 類將會捕獲錯誤信息。
validate() 方法所做的第一件事就是創建用來返回驗證錯誤的ActionErrors 對象。
ActionErrors errors = new ActionErrors();
如果返回一個非空ActionErrors 對象,該方法會將錯誤通知給請求處理器。如果沒有ActionError添加進來,ActionErrors將是空的。該方法可能返回空ActionErrors ,也可能返回null來表示沒有錯誤發生。
在本例中,如果發生錯誤將會創建一個新的ActionError 對象並將其添加到ActionErrors 中:
errors.add("typeError", new ActionError("TypeError",type));
我們將在例子中創建一個含有名爲"typeError"的屬性的新錯誤。<netui:error> 標籤使用名稱來判斷應該報告哪個錯誤。新創建的ActionError 含有一個主鍵 "TypeError"。主鍵是在消息包中查找錯誤顯示信息要用到的ID。除此之外,我們還會傳遞變量type以便在信息創建時進行文本替換。
步驟2——在動作中打開驗證
只有爲動作開啓了驗證功能,validate() 方法才能被調用。通過添加一個validation-error-page 屬性到@jfp:action 註解即可開啓驗證功能:
/**
* @jpf:action validation-error-page="index.jsp"
* @jpf:forward name="success" path="results.jsp"
*/
protected Forward processNameBean(NameBean form)
{ HttpServletRequest req = getRequest();
req.setAttribute("Form",form);
return new Forward("success");
}
既可以手工也可以通過動作源碼視圖的屬性編輯窗口來定義註解。
圖2——動作源代碼視圖屬性編輯窗口
上面,我們把validation-error-page 設置成index.jsp。這將開啓該動作的驗證功能。如果發生驗證錯誤,將會顯示index.jsp。
步驟3——創建錯誤消息文本
錯誤消息文本是從消息包中查找出來的。消息包被放置在WEB-INF/classes子目錄下。在本例中,我們創建Validation.properties 文件並將其放在 WEB-INF/classes/message 目錄,請參見圖3。
圖3——Validation.properties
Validation.properties 中包含那些將會顯示在<netui:error> 或 <netui:errors> 標籤中的文本。該屬性文件包含以下條目:
errors.header=<ul>
errors.footer=</u> errors.prefix=<li> errors.suffix</li>
NullNameError=The name field must not be null. UpperCaseNameError=The name <b>'{0}'</b> must begin with an upper case letter.
TypeError=The type <b>'{0}'</b> must be the value 'foo' or 'bar' 在例子中,我們通過以下語句創建一個 ActionError :
ActionError("TypeError",type)); |
字符串“TypeError”用於消息的查找:
TypeError=The type <b>'{0}'</b> must be the value 'foo' or 'bar' |
在運行時刻,變量type 替換'{0}'處從而生成報告給用戶的完整文本。
步驟4—— 將消息文件綁定到頁面流
消息文件Validation.properties 需要綁定到頁面流,這樣才能在報告消息給用戶之前進行消息的查找。可以通過頁面流的一個註解來進行綁定。
* @jpf:controller { |
@jpf:message-resources註解聲明頁面流使用messages.Validation 屬性文件。既可以在源代碼中直接設置該註解,也可以在源碼視圖頁面流屬性編輯窗口下面的消息資源列表區域中設置。
圖4——源代碼視圖頁面流屬性編輯窗口
消息資源名稱是查找消息包需要用到的classpath條目。本例中,因爲WEB-INF/classes 在classpath中,所以messages.Validation能夠被找到。它的實際文件名是WEB-INF/classes/message/Validation.properties。
注意:Java將會緩存那些訪問過的消息包。如果在開發過程中修改了.properties 文件,可以通過修改頁面流文件並重啓測試瀏覽器、或停止並重啓服務來清空緩存。
步驟5——在JSP頁面中報告錯誤
有兩個頁面流標籤用於報告驗證過程中發生的錯誤。<netui:error> 標籤根據value 屬性進行錯誤報告。它通常被用在輸入字段後面直接報告該字段的錯誤。<netui:errors> 標籤將會報告頁面中的所有錯誤。下面的JSP代碼片斷演示了一個<netui:form>例子,它負責提交NameBean表單bean。它使用<netui:error> 標籤在用戶輸入字段之後報告錯誤。除此之外,通過<netui:errors> 標籤,其它錯誤被報告在文檔的底部。
<h4>Name Input Form</h4> <netui:form action="processNameBean" focus="">
<table>
<tr>
<td><netui:label value="Name:"> </netui:label></td> <td><netui:textbox dataSource="{actionForm.name}"> </netui:textBox></td> <td><span style="color:#cc0000;"> <netui:error value="nameError"/></span></td> </tr> <tr> <td><netui:label value="Type:"/></td> <td><netui:textBox dataSource="{actionForm.type}"/></td> <td><span style="color:#cc0000;"> <netui:error value="typeError"/></span></td> </tr> </table>
<netui:button value="Submit Form"></netui:button> </netui:form> <netui-template:visible visibility="{request.errorNotSet}" negate="true" > <hr /> <span style="color:#cc0000;"> <netui:errors><br /></netui:errors> </span> </netui-template:visible> |
<netui:errors> 標籤有四個可以定義在消息包中的特殊屬性:errors.header, errors.footer, errors.prefix, errors.suffi,利用這些屬性可以在顯示的消息周圍進行格式設定。在本例中,錯誤被顯示在一個未排序的列表中。而且,<netui:error> 標籤允許error.prefix 和error.suffix 定義標記性的標籤。本例中沒有演示這一功能。
結果
圖5是validate()方法生成一堆驗證錯誤之後向用戶顯示的頁面。用戶在表單中輸入非法數據後驗證錯誤被返回,然後請求處理器定向到index.jsp頁面,錯誤信息被顯示在字段之後以及表單下面。錯誤表單的內容全部被返回,這樣用戶可以只修改那些出錯的字段。
圖5——帶有驗證錯誤的HTML頁
結論
服務端的用戶輸入驗證需要經歷五個步驟。首先在表單bean中創建一個validate() 方法,該方法將會驗證用戶輸入並報告錯誤。第二步,必須在接收表單的動作上設置validation-error-page 符號才能激活表單驗證。第三步,添加錯誤文本到消息包中。第四步,藉助消息資源註解,消息包被綁定到頁面流上。最後,在JSP中添加<netui:error> 或 <netui:errors> 標籤,它們將會顯示錯誤文本。頁面流框架會管理所有的驗證調用,並在錯誤發生後將控制權路由到輸入頁面,否則路由到目標動作。