建立Web應用的過程中,驗證使用者在HTML表單中的輸入是最常見的一個問題。使用者輸入資料必須被驗證從而保證進行業務處理之前資料是正確的。輸入的表單資料可能用於建立或更新資料庫條目、調用Web服務、或作為業務過程的輸入。基於目前由Struts提供的表單驗證架構,頁面流提供了一種簡單易用的服務端驗證方式。
頁面流驗證提供以下功能:
· 使用者表單輸入的服務端驗證
· 用待驗證的表單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——驗證方法
驗證使用者輸入的第一個步驟是在表單bean上建立validate()方法。該方法將會檢查表單bean的屬性,察看有沒有使用者輸入錯誤。下面這個非常簡單的表單bean定義了兩個屬性:name 和type。validate() 方法提供了對這些屬性的驗證。
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"); }
既可以手工也可以通過動作源碼視圖的屬性編輯視窗來定義註解。
步驟3——建立錯誤訊息文本
錯誤訊息文本是從訊息包中尋找出來的。訊息包被放置在WEB-INF/classes子目錄下。在本例中,我們建立Validation.properties 檔案並將其放在 WEB-INF/classes/message 目錄
Validation.properties 中包含那些將會顯示在<netui:error> 或 <netui:errors> 標籤中的文本。該屬性檔案包含以下條目:
rrors.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 resources="messages.Validation"
*/
public class Controller extends PageFlowController
{
@jpf:message-resources註解聲明頁面流使用messages.Validation 屬性檔案。既可以在原始碼中直接設定該註解,也可以在源碼視圖頁面流屬性編輯視窗下面的訊息資源清單地區中設定。
訊息資源名稱是尋找訊息包需要用到的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 定義標記性的標籤。本例中沒有示範這一功能。
結果
使用者在表單中輸入非法資料後驗證錯誤被返回,然後要求處理常式定向到index.jsp頁面,錯誤資訊被顯示在欄位之後以及表單下面。錯誤表單的內容全部被返回,這樣使用者可以只修改那些出錯的欄位。
結論
服務端的使用者輸入驗證需要經曆五個步驟。首先在表單bean中建立一個validate() 方法,該方法將會驗證使用者輸入並報告錯誤。第二步,必須在接收表單的動作上設定validation-error-page 符號才能啟用表單驗證。第三步,添加錯誤文本到訊息包中。第四步,藉助訊息資源註解,訊息包被綁定到頁面流上。最後,在JSP中添加<netui:error> 或 <netui:errors> 標籤,它們將會顯示錯誤文本。頁面流架構會管理所有的驗證調用,並在錯誤發生後將控制權路由到輸入頁面,否則路由到目標動作。