驗證使用者的資料是否正確,對於Web應用程式來說是相當重要的,尤其是對一些私密資源的保護,只允許通過驗證的使用者觀看,這種需求對Web應用程式來說處處可見。
驗證有很多形式,這邊針對兩個部份,一個是資料完整性驗證,一個是資料正確性驗證。
- 資料完整性
指的是驗證使用者是否輸入了應用程式所要求的所有訊息,以及訊息的格式是否符合程式要求等等,例如應用程式可能要求使用者同時提供使用者名稱與密碼,並且密碼長度不得少於6個字元。
      
      - 資料正確性驗證
是在通過完整性驗證之後的檢查,例如應用程式檢查使用者提供的使用者名稱與密碼是否符合資料庫中的資料。
      
      另外,驗證的地點可以是在伺服端,也可以是在客戶端。
- 客戶端
通常是透過JavaScript來完成,驗證工作直接在客戶端進行,不用傳回給伺服端,可以節省網路與伺服器資源。
      
      - 伺服端
只依賴客戶端驗證是不夠的,畢竟客戶端可能跳過它,所以伺服端也必須驗證資料,這邊要介紹的就是如何使用Struts的表單物件進行伺服端資料驗證。
      
      其實很簡單,只要在繼承 ActionForm 之後,重新定義其validate()方法即可,不過這邊要繞個彎,在不使用Struts標籤的情況下要如何進行這項工作,這麼作的目的,在於讓您更瞭解Struts的運作流程。
如果您不想繞彎,記得下面的設定若配合Struts <html:messages> 標籤的話會很方便的達成。
首先來看看表單物件如何撰寫:
- UserForm.java
package onlyfun.caterpillar; 
 
import java.util.*;
import javax.servlet.http.*; 
import org.apache.struts.action.*;
import org.apache.struts.Globals;
import org.apache.struts.util.MessageResources;
public class UserForm extends ActionForm {
    private String username;
    private String password;
    
    public void setUsername(String username) { 
        this.username = username; 
    } 
    
    public void setPassword(String password) { 
        this.password = password; 
    } 
    
    public String getUsername() { 
        return username; 
    }
    
    public String getPassword() {
        return password;
    } 
    
    public void reset(ActionMapping mapping, 
                      HttpServletRequest req) {
        username = null;
        password = null;
    }
    
    public ActionErrors validate(ActionMapping mapping, 
            HttpServletRequest request) {
        
        Map errModel = new HashMap();
        MessageResources messageResources =
            (MessageResources) request.getAttribute(
                                    Globals.MESSAGES_KEY);
        
        if(getUsername() == null || 
                getUsername().length() < 1) {
            String msg = 
               messageResources.getMessage(
                                "error.invalidUsername");
            errModel.put("invalidUsername", msg);
        }
        
        if(getPassword() == null || 
                getPassword().length() < 1) { 
            String msg = 
               messageResources.getMessage(
                               "error.invalidPassword");
            errModel.put("invalidPassword", msg);
        }
        
        if(errModel.get("invalidUsername") == null &&
           errModel.get("invalidPassword") == null) {
            // no error happened
            // return null to proceed the Action
            return null;
        }
        else { 
            request.setAttribute("errors", errModel);
            
            // fake codes, just tell RequestProcessor
            // not to invoke Action
            ActionErrors errors = new ActionErrors(); 
            errors.add(ActionMessages.GLOBAL_MESSAGE, 
                    new ActionMessage(""));
            return errors;
        }
    }
}主要看看validate()方法,在這邊從request中取得了 MessageResources ,這是為了能取得訊息資源檔中的訊息設定,程式中使用一個Map物件來記錄我們所發現的錯誤訊息,最後將之設定給request。
validate()方法要傳回一個ActionErrors物件,它是ActionMesssage的子類別,如果想直接使用它們,搭配 Struts <html:messages>標籤會比較方便,但這邊先不打算使用。
RequestProcessor 根據validate()傳回的ActionErrors是否為null或其中是否包括有ActionMessage物件,以判斷是不是要進一步呼叫 Action 物件,如果發現傳回的不是null或是包括有ActionMessage,中斷接下來呼叫Action的流程,而直接跳到您所設定好的驗證錯誤頁面。
雖然這邊不打算使用<html:messages>標籤,但希望上面的流程可以正常運作,所以當發現有錯誤時,仍然回傳一個 ActionErrors物件,這只是用來滿足RequestProcessor完成上述流程運作所需要的條件。。
當傳回的ActionErrors不為null,則會跳至您所設定的驗證錯誤頁面,這是在struts-config.xml中設定:
- struts-config.xml
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
    <form-beans> 
        <form-bean 
            name="userForm" 
            type="onlyfun.caterpillar.UserForm"/> 
    </form-beans>
    <action-mappings> 
        <action 
            path="/login" 
            type="onlyfun.caterpillar.LoginAction"
            name="userForm"
            validate="true"
            input="/WEB-INF/pages/fail.jsp">
             
            <forward
                name="helloUser" 
                path="/WEB-INF/pages/hello.jsp"/>
            <forward
                name="loginFail" 
                path="/WEB-INF/pages/fail.jsp"/>
        </action>         
    </action-mappings> 
    
    <message-resources parameter="resources/messages"/>
</struts-config>您必須設定<action>的validate屬性為true,這樣RequestProcessor才會執行validate()方法, <action>的input屬性則是讓您設定當驗證錯誤時,應該導向的頁面。
進一步的,看看LoginAction中要作的正確性驗證,它檢查使用者名稱與密碼是否符合設定:
- LoginAction.java
package onlyfun.caterpillar;
import java.util.*;
import javax.servlet.http.*; 
import org.apache.struts.action.*; 
import org.apache.struts.util.MessageResources;
import org.apache.commons.beanutils.PropertyUtils;
public class LoginAction extends Action {
    public ActionForward execute(ActionMapping mapping,
                             ActionForm form,
                             HttpServletRequest request, 
                             HttpServletResponse response) 
                                throws Exception { 
        
        String username = (String) 
                  PropertyUtils.getSimpleProperty(
                                     form, "username"); 
        String password = (String) 
                  PropertyUtils.getSimpleProperty(
                                     form, "password"); 
        
        request.setAttribute("username", username);
        
        if(username.equals("caterpillar") && 
                  password.equals("1234")) {
            return mapping.findForward("helloUser"); 
        }
        
        Map msgModel = new HashMap();
        
        MessageResources messageResources = 
            (MessageResources) getResources(request);
        
        msgModel.put("namePasswordMismatched", 
                messageResources.getMessage(
                        "message.namePasswordMismatched"));
        
        request.setAttribute("messages", msgModel);
        
        return mapping.findForward("loginFail"); 
    }
}這邊使用了PropertyUtils輔助類別,這可以很方便的幫您取出表單物件中的屬性。這邊還需要一個訊息檔案來管理訊息:
- message_zh_TW.properties
# errors
error.invalidUsername=使用者名稱不得為空!
error.invalidPassword=密碼不得為空!
 
# messages
message.namePasswordMismatched=使用者名稱或密碼輸入錯誤!來看看fail.jsp,這邊並沒有使用Struts標籤,所以只要如下取出訊息即可:
- fail.jsp
<html>
<head> 
<title>Sorry!</title> 
</head> 
<body>
    <H1>
    \${errors["invalidUsername"]}<br>
    \${errors["invalidPassword"]}<br>
    \${messages["namePasswordMismatched"]}
    
    </H1>
    <p>
    <a href='/strutsapp/html/form.htm'>Login</a>
</body> 
</html>form.htm與hello.jsp就使用 使用 ActionForm 中介紹過的即可。
在不使用ActionMessage與Struts標籤的情況下,撰寫這樣的程式好像複雜了一些,如果您想省點功夫,您可以考慮搭配 <html:messages> 來撰寫這個程式。

