1.數(shù)據(jù)校驗
Struts數(shù)據(jù)效驗, 通過攔截器完成
<interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/>
1.1 Struts2進行表單驗證(手工方式)
a.配置表單
<form method="post" action="${pageContext.request.contextPath }/user_register.action">
用戶名:<input type="text" name="user.userName">
<s:fielderror fieldName="user.userName"></s:fielderror>
<br/>
密碼:<input type="text" name="user.pwd">
<s:fielderror fieldName="user.pwd"></s:fielderror>
<br/>
郵箱:<input type="text" name="user.email"><br/>
生日:<input type="text" name="user.birth"><br/>
<input type="submit" value="注冊"><br/>
</form>
b.UserAction.java
//b.1 代碼方式驗證Action中所有的方法
//注意:如果要想用struts的數(shù)據(jù)效驗功能,必須繼承ActionSupport或?qū)崿F(xiàn)相關(guān)接口
public class UserAction extends ActionSupport {
// 封裝請求數(shù)據(jù)
private User user = new User();
public void setUser(User user) {
this.user = user;
}
public User getUser() {
return user;
}
// 重寫數(shù)據(jù)驗證的方法
@Override
public void validate() {
// 用戶名非空
if (user.getUserName() == null || "".equals(user.getUserName())) {
// 保存錯誤信息
super.addFieldError("userName", "用戶名必須填寫!");
}
// 密碼
if (user.getPwd() == null || "".equals(user.getPwd())) {
super.addFieldError("pwd", "密碼必填");
}
}
}
b.2 代碼方式驗證Action中指定的方法
寫驗證方法命名規(guī)則:
validate + 要驗證的方法名
如:
public void validateRegister() {}
只會驗證當(dāng)前action的register方法!
總結(jié)代碼方式驗證:
繁瑣,設(shè)計很多重復(fù)的驗證邏輯!例如:非空驗證、數(shù)值驗證、email、日期等。
1.2 XML方式驗證Action中所有的方法
Struts對于常用的驗證,進行了封裝,即提供了驗證器, 驗證指定的常用業(yè)務(wù)邏輯!
Struts提供的所有驗證器
路徑:xwork-core-2.3.4.1.jar/com.opensymphony.xwork2.validator.validators/default.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator Definition 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd">
<!-- START SNIPPET: validators-default -->
<validators>
<validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
<validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>
<validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/>
<validator name="long" class="com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator"/>
<validator name="short" class="com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator"/>
<validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/>
<validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/>
<validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/>
<validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/>
<validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/>
<validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/>
<validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/>
<validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/>
<validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>
<validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/>
<validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/>
</validators>
<!-- END SNIPPET: validators-default -->
如果寫xml,從而定義驗證規(guī)則:
1)XML文件名稱語法: ActionClassName-validation.xml
注意:此xml需要與當(dāng)期要驗證的action在同一個目錄:
舉例:UserAction-validation.xml
2) 寫XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<validators>
<!-- 驗證的每一個字段用field表示 -->
<field name="user.userName">
<!-- 指定使用的驗證器 -->
<field-validator type="requiredstring">
<!-- 驗證失敗的錯誤提示信息 -->
<message>用戶名不能為空!</message>
</field-validator>
</field>
<!-- 驗證pwd -->
<field name="user.pwd">
<!-- 非空 -->
<field-validator type="requiredstring">
<message>密碼不能為空!</message>
</field-validator>
<!-- 長度 -->
<field-validator type="stringlength">
<param name="minLength">6</param>
<param name="maxLength">8</param>
<message>密碼必須為6-8位!</message>
</field-validator>
</field>
<!-- 驗證日期 -->
<field name="user.birth">
<field-validator type="date">
<message>日期格式不對!</message>
</field-validator>
</field>
<!-- 驗證Email -->
<field name="user.email">
<field-validator type="email">
<message>郵箱格式錯誤!</message>
</field-validator>
</field>
</validators>
如果驗證Action中指定的方法:
與上面xml驗證方式大致相同: 驗證xml文件內(nèi)容不變;
文件命名:
語法:ActionClassName-ActionName-validation.xml
舉例:UserAction-user_register-validation.xml
驗證UserAction中的register方法
2.數(shù)據(jù)回顯
數(shù)據(jù)回顯,必須要用struts標(biāo)簽!
Action中:
// 進入修改頁面
public String viewUpdate() {
// 模擬一個對象(先獲取一個id,再根據(jù)id調(diào)用service查詢,把查到的結(jié)果保存到域)
User userInfo = new User();
userInfo.setUserName("Jack");
userInfo.setEmail("yuanjie@itcast.cn");
ActionContext ac = ActionContext.getContext();
// Map<String,Object> request = (Map<String, Object>) ac.get("request");
// request.put("userInfo", userInfo);
/************* 數(shù)據(jù)回顯***************/
// 獲取值棧
ValueStack vs = ac.getValueStack();
vs.pop();// 移除棧頂元素
vs.push(userInfo); // 入棧
// 進入修改頁面
return "viewUpdate";
}
JSP頁面:
<body>
<%@taglib uri="/struts-tags" prefix="s" %>
<br/>
<!-- 在頁面文本框內(nèi),顯示要修改記錄的數(shù)據(jù) -->
<!-- 手動通過value設(shè)置顯示的值
<s:form action="#">
用戶名: <s:textfield name="user.userName" value="%{#request.userInfo.userName}"></s:textfield> <br/>
郵箱: <s:textfield name="user.email" value="%{#request.userInfo.email}"></s:textfield> <br/>
</s:form>
-->
<!-- 數(shù)據(jù)回顯技術(shù):s:textfield會自動查找根元素數(shù)據(jù)(Ognl表達式語言取值) -->
<s:form action="#">
用戶名: <s:textfield name="userName"></s:textfield> <br/>
郵箱: <s:textfield name="email"></s:textfield> <br/>
</s:form>
<s:debug></s:debug>
</body>
3. 模型驅(qū)動
Struts運行時候,會執(zhí)行默認的攔截器棧,其中有一個攔截器,模型驅(qū)動攔截器,可以把請求數(shù)據(jù)自動填充的action的屬性中
<interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
實例:
JSP
<input type=text name=userName />
<input type=text name=pwd />
Action
- 實現(xiàn)ModelDriver接口
- 實現(xiàn)接口方法: 接口方法返回的就是要封裝的對象
- 對象一定要實例化。
public class UserAction extends ActionSupport implements ModelDriven<User> {
// 封裝請求數(shù)據(jù)
private User user = new User();
public void setUser(User user) {
this.user = user;
}
public User getUser() {
return user;
}
// 實現(xiàn)模型驅(qū)動接口方法
@Override
public User getModel() {
return user;
}
public String add() {
// 測試: 使用了模型驅(qū)動,是否數(shù)據(jù)正常? Ok
System.out.println(user);
return "success";
}
}
4.防止表單重復(fù)提交
Struts提供了防止表單重復(fù)提交攔截器:
<interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
用戶重復(fù)提交表單在某些場合將會造成非常嚴(yán)重的后果。例如,在使用信用卡進行在線支付的時候,如果服務(wù)器的響應(yīng)速度太慢,用戶有可能會多次點擊提交按鈕,而這可能導(dǎo)致那張信用卡上的金額被消費了多次。因此,重復(fù)提交表單會對你的系統(tǒng)帶來邏輯影響,必須采取一些措施防止這類情況的發(fā)生。
用戶重復(fù)提交同一個HTML表單的原因有:
一、快速多次點擊了提交按鈕;
二、提交表單后按下瀏覽器的刷新按鈕。
Struts 2已經(jīng)內(nèi)置了能夠防止用戶重復(fù)提交同一個HTML表單的功能。它的工作原理:讓服務(wù)器生成一個唯一標(biāo)記,并在服務(wù)器和表單里各保存一份這個標(biāo)記的副本。此后,在用戶提交表單的時候,表單里的標(biāo)記將隨著其他請求參數(shù)一起發(fā)送到服務(wù)器,服務(wù)器將對他收到的標(biāo)記和它留存的標(biāo)記進行比較。如果兩者匹配,這次提交的表單被認為是有效的,服務(wù)器將對之做出必要的處理并重新設(shè)置一個新標(biāo)記。隨后,提交相同的表單就會失敗,因為服務(wù)器上的標(biāo)記已經(jīng)重置。
Struts 2標(biāo)簽中的token標(biāo)簽,可以用來生成一個獨一無二的標(biāo)記。這個標(biāo)記必須嵌套在form標(biāo)簽中使用,它會在表單里插入一個隱藏字段并把標(biāo)記保存到HttpSession對象里。toke標(biāo)簽必須與Token或Token Session攔截器配合使用,兩個攔截器都能對token標(biāo)簽進行處理。Token攔截器遇到重復(fù)提交表單的情況,會返回一個"invalid.token"結(jié)果并加上一個動作級別的錯誤。Token Session攔截器擴展了Token攔截器并提供了一種更復(fù)雜的服務(wù),它采取的做法與Token攔截器不同,它只是阻斷了后續(xù)的提交,這樣用戶不提交多少次,就好像只是提交了一次。
示例:使用Token攔截器預(yù)防表單重復(fù)提交
4.1 配置struts.xml文件,聲明動作
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="avoidPackage" extends="struts-default">
<action name="avoid" class="struts2.action.AvoidAction">
<interceptor-ref name="token"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<result name="invalid.token">/error.jsp</result>
<result name="input">/input.jsp</result>
<result name="success">/output.jsp</result>
</action>
</package>
</struts>
此時,需要在動作的聲明中,為動作添加token攔截器,因為token攔截器不在defaultStack攔截器棧中,注意,需要將攔截器放在攔截器棧的第一位,這是因為判斷表單是否被重復(fù)提交的邏輯應(yīng)該在表單處理前。
4.2 創(chuàng)建動作類
public class AvoidAction extends ActionSupport {
private static final long serialVersionUID = 2676453800249807631L;
private String username;
private Date birthday;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String execute()
{
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return SUCCESS;
}
}
這個動作邏輯處理為掛起4秒鐘,讓我們有機會多次點擊提交按鈕,測試效果。
4.4 創(chuàng)建頁面:
input.jsp
<s:form action="avoid">
<s:token></s:token>
<s:textfield name="username" label="Enter your name"></s:textfield>
<s:textfield name="birthday" label="Enter your birthday"></s:textfield>
<s:submit value="submit"></s:submit>
</s:form>
要使用Struts 2的防止表單重復(fù)提交功能,需要在form標(biāo)簽中使用token標(biāo)簽,他會產(chǎn)生一個唯一的標(biāo)識符,與其他參數(shù)一起提交到服務(wù)器,服務(wù)器會根據(jù)token標(biāo)簽所產(chǎn)生的標(biāo)識符判斷表單是否為重復(fù)提交的表單,這個功能是由Token攔截器完成的。
error.jsp
<body>
do not duplicate submissions form!
</body>
當(dāng)表單重復(fù)提交,Token攔截器會返回一個"invalid.token"結(jié)果,結(jié)果將頁面轉(zhuǎn)到這個頁面,提示用戶錯誤信息。
output.jsp
<body>
Your Name : <s:property value="username"/>
<br />
Your Birthday : <s:property value="birthday"/>
</body>
4.5 測試
在瀏覽器中輸入:http://localhost:8080/struts04/input.jsp,輸入信息得到如下界面

連續(xù)多次點擊"submit"按鈕,查看效果

可以看到,token攔截器的設(shè)置生效了,他阻止了表單的重復(fù)提交,并給出了錯誤提示
這次我們只點擊一次提交(請重新輸入URL,或后退到輸入頁面后刷新一下,這是因為token的標(biāo)示在提交一次后已被修改,不刷新標(biāo)示符是不可能與服務(wù)器存留的標(biāo)示符一致的)

可以看到,表單被正確的處理了。