今天是Struts1的第二天,明天是最后一天。看我這么說,多少感覺Struts1有點煩。因為總是使用配置文件,不用怎么寫代碼,一個強大的表單校驗功能就完成了。其實我們一直專注于代碼的重用,減少重復工作所帶來的枯燥與麻煩,從這個角度上想,Struts是相當好的!
下面我們來看一下今天的重點內容:
一、復雜驗證
什么是復雜驗證?相比昨天的簡單驗證(ActionForm)復雜在哪里呢?其一點也不復雜,一說便知。昨天的簡單驗證是對瀏覽器提交的Form表單信息的驗證,比如用戶注冊時輸入的用戶名、密碼、生日、郵箱等信息是否合法。而復雜驗證,就是對業務邏輯的驗證,比如在Action中調用業務邏輯的DAO,驗證用戶的登錄信息是否有效。
例如,在昨天的練習中添加一個用戶登陸頁面“login.jsp”,添加一個處理用戶登陸的Action“LoginAction”(其實它可以與昨天的用戶注冊Action放到一起,后邊再介紹),再添加一個用戶DAO“UserDao”。
login.jsp, 下面為主體部分:
<body> <table align="center"> <html:form action="${pageContext.request.contextPath }/lgoin.do"> <tr> <td>用戶名:</td> <td><html:text property="username" /></td> </tr> <tr> <td>密碼:</td><td><html:password property="password"/></td> </tr> <tr> <td colspan="2"><html:submit value="登陸"/></td> </tr> </html:form> </table> </body> |
發現“<html:xxx…/>”沒有?這是Struts常用的Html標簽,下面會有介紹!
LoginAction.java,下面為主體部分
public class LoginAction extends Action { @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // 獲取用戶提交的信息 String username = request.getParameter("username"); String password = request.getParameter("passowrd"); // 調用UserDao查找用戶 UserDao ud = new UserDao(); User user = ud.find(username, password); // 根據查找結果返回對應信息 if(user == null){ // 創建錯誤信息,也可以使用ActionErrors。 ActionMessages errors = new ActionMessages(); ActionMessage message = new ActionMessage("errors.login.worng"); errors.add("loginwrong", message); // 將信息保存到request或session中。 this.saveErrors(request, errors); // 跳轉到錯誤頁面 return mapping.getInputForward(); } return mapping.findForward("success"); } } |
其中的UserDao就不用列出來了,因為以前做的很多地方都有提到它。
登陸成功,直接跳轉到昨天添加的“success.jsp”頁面,顯示歡迎信息。如果失敗,則跳轉回登陸頁面,并使用“<html:errors/>”顯示其錯誤信息。在此就不列出了。
注意一定要在struts-config.xml中配置這個新添加的LoginAction,具體信息如下:
<action path="/lgoin" type="cn.itcast.cc.actions.LoginAction" input="/login.jsp"> <forward name="success" path="/success.jsp"></forward> </action> |
昨天落下了一個知識點,信息資源文件。有沒有發現,昨天的代碼和今天的代碼中有類似“ActionMessage("errors.login.worng")”的內容,其中的“"errors.login.worng"”是屬性文件的key。這個屬性文件是通過struts-config.xml配置的:
<message-resources parameter="MessageResources"></message-resources> |
Struts框架根據i18n的文件名稱要求,自動加載配置文件,所有的提示信息都可以在這里設置。
Ok,上邊就是一個復雜驗證的簡單示例。至于到底有多復雜,視業務邏輯的復雜度而定。
二、處理Struts的中文亂碼問題
1.在Action的execute方法中有一個“HttpServletRequest”參數,使用這個參數設置編碼格式可不可以?“request.setCharacterEncoding("UTF-8");”。No,如果request的getParamter方法被調用過,setCharacterEncoding就無效。在什么地方調用了getParamter方法?ActionForm中調用了啊!
2.編寫一個filter,在這學習過濾器時已經介紹了。是一個好的解決辦法。
3.我們不是在1中說了,是ActionForm調用了getParameter方法嗎?那就在getParameter之前調用setCharacterEncoding設置編碼。ActionForm之前是什么?是ActionSerlvet,嗯,對的。我們需要使用繼承重寫ActionServlet,并覆蓋ActionServlet的process方法,在process方法里設置編碼。然后修改web.xml的action名稱的Servlet,使它的類指向我們自定義的ActionServlet。
4.除了通過編寫自己的ActionServlet設置request的編碼,也可以編寫自己的RequestProcessor。什么是RequestProcessor?ActionServlet的process方法調用了RequestProcessor方法,RequestProcessor是ActionServlet控制器的核心。所以我們編寫自己的RequestProcessor方法,并覆蓋它的process方法,在process方法中設置request的編碼。完成后,需要在struts-config.xml添加:
<controller processorClass="cn.itcast.cc.servlet.MyRequestProcessor"/> |
上邊沒什么復雜的,我就不一一實現了!
三、使用Struts處理表單重復提交的問題和Struts的HTML標簽
什么是表單重復提交?概括一下:在一個會話中,重復向服務器提交表單數據!這就是表單重復提交。比如,在當前頁面下連續點擊提交按鈕(但頁面一旦被刷新就啟動了一個新會話)。
在以前學習Session時,也有講過這一問題。這一問題的處理辦法:
1. 編寫一個JavaScript腳本,當用戶點擊提交按鈕時。使用按鈕變灰(禁用),或設置一個標記,用戶無法點擊,或再次點擊時判斷這個標記值。來防止用戶重復提交表單數據。
2. 在用戶向服務器請求信息時,比如請求注冊頁面時。在服務器生成一個全球唯一標識符碼,將標識符碼同時保存到Session和頁面的隱藏字段中。當用戶提交注冊信息時,對比Session和隱藏字段中的標識碼,如果相同說明是用戶第一次提交,此時,將Session中的標記碼刪除掉。當用戶再次提交注冊信息時,標識碼已經不相同了(因為sesssion中的已經被刪除了),所以就拒絕這個重復的請求。
3. 今天我們學習一個新的方便快捷的辦法,處理表單重復提交的問題。此處連同struts的Html標簽一同介紹了。
1).我們修改昨天的注冊頁面的Body體內容為:
<body> <table align="center"> <html:form> <tr> <td>用戶名:</td> <td><html:text property="username" /></td> </tr> <tr> <td>密碼:</td> <td><html:password property="password" /></td> </tr> <tr> <td>確認密碼:</td> <td><html:password property="password1" /></td> </tr> <tr> <td>生日:</td> <td><html:text property="birthday" "/></td> </tr> <tr> <td><html:submit value="注冊"/></td> <td><html:reset value="重填"/></td> </tr> </html:form> </table> </body> |
使用struts的HTML標簽有什么好處?
1. 驗證表單錯誤,表單回顯。
2. 將表單與ActionForm進行校驗,如果出現錯誤就拋異常。昨天沒有使用struts的HTML標簽時,是不會拋異常的。
注意:如果表單中存在“<html:checkbox/>”選項,它是不會被回顯的,應當使用:<html:multibox property="interesting" value="reading"></html:multibox>
2).在加載表單之前調用“TokenProcessor.saveToken()”方法,它會自動向表單和Session中添加全球唯一標識符(自動創建表單中的隱藏字段)。比如,我們可以在跳轉到JSP頁面的Action中添加“TokenProcessor.saveToken()”方法,也可以在JSP頁面的表單之前添加““TokenProcessor.saveToken()”方法”。這里我們在JSP頁面的表單之前添加:
<% org.apache.struts.util.TokenProcessor.getInstance().saveToken(request); %> |
3).啟動服務器,訪問注冊頁面。查看注冊頁面的源代碼,發現自動生成了以下部分:
<form name="regForm" method="post" action="/091220StrutsLogin/reg.do;jsessionid=13ADEF2ADC0126D626FBE49EC98F5EFF"> <div> <input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="cb279a07b286c393c7c1081a49e44313"> </div> … |
其中 form 中的name、method、jsessionid和<div>中的內容是自動添加的。
Name是表單ActionForm的名字,Sturts使用它與ActionForm進行校驗。
Method是表單的提交方式。
Jsessionid當第一次請求注冊頁面時,或cookie被禁用時。Sturts框架會自動進行URL重寫。
<div>中的內容是防止表單重復提交。
4).在處理注冊信息的Action中調用“TokenProcessor.isTokenValid(request)”方法用于判斷表單是否重復提交,返回真為第一次提交,返回假為重復提交。然后調用“TokenProcessor.getInstance().resetToken(request);”方法清除seission的防止表單重復提交的信息,從此以后isTokenValid方法返回假。
常用的Struts HTML標簽(老佟的):
標簽 | 用途/注解 |
html | 產生一個<html>標簽。也包括來自于用戶會話中的 language 屬性 |
form | 定義一個表單。Action 和 focus 屬性是最有用的屬性 |
checkbox | 產生一個檢查框字段 |
file | 產生一個文件選擇輸入字段 |
hidden | 產生一個隱藏字段 |
option | 產生一個選擇項 |
options | 產生一個選擇項列表 |
password | 產生一個口令輸入字段 |
radio | 產生一個單選輸入字段 |
select | 產生一個選擇元素 |
text | 產生一個文本輸入字段 |
textarea | 產生一個 html 文本區域元素 |
image | 產生一個圖像輸入字段 |
button | 產生一個按鈕輸入字段 |
cancel | 產生一個取消按鈕 |
submit | 產生一個提交按鈕 |
reset | 產生一個重新設定按鈕 |
errors | 顯示錯誤消息 |
img | 產生一個 html img 標簽 |
四、使用Struts的validator插件進行簡單的表單驗證
之前我們使用的是ActionForm進行表單的簡單驗證,在那里我們需要手動的進行各個字段的合法性驗證,這是一個重復的工作,而且十分無聊。Validator插件,就是用于解決這個問題的。
1.在struts中引入插件,需要向struts-config.xml文件添加:
<plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/org/apache/struts/validator/validator-rules.xml, /WEB-INF/validation.xml" /> </plug-in> |
其中pathnames屬性的兩個值:
1).“/org/apache/struts/validator/validator-rules.xml”中定義了Validator提供的字段約束。validator-rules.xml文件在struts-core-1.3.8.jar的org.apache. struts.validator包中。
2).“/WEB-INF/validation.xml”是Validator的配置文件。
2.如果你知道這個文件內容應該是什么樣子。可以到struts包的apps目錄下,解壓縮struts-cookbook-1.3.8.war,在它的WEB-INF目錄下就有一個validation.xml文件。我們向“WEB-INF”目錄下添加“validation.xml”文件,并添加內容:
<form-validation> <formset> <form name="validatorForm"> <!—- 用戶名 --> <field property="username" depends="minlength"> <arg key="validator.min" position="0"/> <arg key="${var:minlength}" resource="false" position="1"/> <var> <var-name>minlength</var-name> <var-value>6</var-value> </var> </field> <!—- 密碼 --> <field property="password" depends="required,minlength"> <arg key="validator.password" position="0" /> <arg key="${var:minlength}" resource="false" position="1" name="minlength"/> <var> <var-name>minlength</var-name> <var-value>5</var-value> </var> </field> <field property="password2" depends="validwhen"> <msg name="validwhen" key="validator.validwhen.password2"/> <var> <var-name>test</var-name> <var-value>(*this*==password)</var-value> </var> </field> <!—- 生日 --> <field property="birthday" depends="date"> <arg key="validator. birthday" /> <var> <var-name>datePattern</var-name> <var-value>yyyy-MM-dd</var-value> </var> </field> </form> </formset> </form-validation> |
1).“<form name="validatorForm">”指定ActionForm,這個ActionForm不需要編寫自己的validate方法。
2).一個<filed></filed>設置一個表單中的字段:
①.“property="username"”指向注冊頁面“<html:text property="username" />”。
②.“<arg key="validator.min" position="0"/>”key指向配置文件MessageResources.properties中自定義的key。Position用于替換第1個位置“{0}”,因為在milength約束中有“msg="errors.minlength"”設置,errors.minlength在MessageResources.properties中自定義。
③.“<arg key="${var:minlength}" resource="false" position="1"/>”,key指向下邊的<var>,resource="false"為不讀取MessageResources.properties文件,替換第2個位置“{1}”。
④. “<var>”定義一個自己的變量,用于設置字符串的最小長度。
⑤.密碼部分就不做詳解了,但其中“<msg name="validwhen" key="validator.validwhen.password2"/>”用于替換“validwhen”的默認錯誤信息。
⑥.生日部分也不做詳解了。
五、DispatchAction,同一類請求綜合處理
同學提問了一個好問題,以前在做練習時,每個請求都單獨寫一個Servlet,現在的Action我們也要這么寫嗎?當然不是!如果開發一個大工程,有上千個請求,難道要寫上千個Action嗎?DispatchAction為我們解決了這個問題。
例,處理用戶注冊、登陸、更新、刪除的這一類請求,我們可以編寫一個DispatchAction:
import javax.servlet.http.*; import org.apache.struts.action.*; import org.apache.struts.actions.DispatchAction; public class TestDispatchAction extends DispatchAction { // add處理 public ActionForward add(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("TestDispatchAction.add"); return mapping.findForward("success"); } // find處理 public ActionForward find(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("TestDispatchAction.find"); return mapping.findForward("success"); } } |
我們需要在struts-config.xml添加如下配置:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN" "http://struts.apache.org/dtds/struts-config_1_3.dtd"> <struts-config> <action-mappings> <action path="/action" type="cn.itcast.cc.actions.TestDispatchAction" parameter="method"> <forward name="success" path="/WEB-INF/pages/success.jsp" /> </action> </action-mappings> </struts-config> |
其中的“parameter="method"”,這里的“method”必須與下面的JSP頁面中的請求參數名稱相同。TestDispatchAction中的方法名稱與參數值相同,參數值指向哪個方法,DispacthAction就調用哪個方法。
JSP頁面:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="html" uri="http://struts.apache.org/tags-html"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <html:link action="/action.do?method=add">add</html:link> <html:link action="/action.do?method=find">find</html:link> </body> </html> |
其中的http://struts.apache.org/tags-html在是菜單:widnow->perferences->XML->XML Catalog添加的,它是指向“struts-1.3.8\docs\dtds\struts-config_1_3.dtd”的URI。
嗯,今天結束!看似內容并不多,但要熟練掌握還要多加練習。
加油!