1. 程式人生 > >Struts2各個功能詳解(2)-輸入校驗和攔截器

Struts2各個功能詳解(2)-輸入校驗和攔截器

param con -- img ava ide xml配置方式 後綴名 voc

前面知道了struts2的架構圖和struts2的自動封裝表單參數和數據類型自動轉換,今天來學struts2的第三第四個東西,輸入校驗和攔截器。

一:輸入校驗

客戶端校驗進行基本校驗,如檢驗非空字段是否為空,數字格式是否正確等。客戶端校驗主要用來過濾用戶的誤操作。作用是:拒絕誤操作輸入提交到服務器處理,降低服務器端負擔。
  服務器端校驗也必不可少,服務器端校驗防止非法數據進去程序,導致程序異常,底層數據庫異常。服務器端校驗是保證程序有效進行及數據完整的手段.
對異常輸入的過濾,就是輸入校驗,也稱為數據校驗
輸入校驗分為客戶端校驗和服務器校驗:
1. 客戶端校驗主要是過濾正常用戶的誤操作,主要通過js代碼完成。


2. 服務器端校驗是整個應用阻止非法數據的最後防線,主要通過在應用中編程實現

我們這個地方學習的是服務器端校驗。

在以前我們寫一個登錄頁面時,並沒有限制用戶的輸入,不管用戶輸入什麽,我們都存入數據庫中,很顯然這是不行的,我們需要檢測用戶輸入的文本是否合法,是否符合我們需要的文本格式,符合才放行,而struts2中就有這種功能,能幫我們在服務器段進行判斷,比如用戶名不能為空,年齡只能在0-100之間等。現在我們就來說說如何使用struts2中的校驗功能。分為兩種,編程式校驗和配置校驗(XML配置校驗)

驗證器的驗證時機:
驗證發生在execute方法之前,在struts2 的params攔截器已經把請求的參數通過反射設置到 Action 的屬性之後,所以,驗證框架實際上驗證的是值棧中的值


驗證的結果:
如果用戶輸入的參數完全滿足驗證結果,那麽會繼續執行execute方法。如果不滿足,會跳轉到Action配置中的result name="input" 的頁面中中

1.1 編程式校驗


技術分享圖片技術分享圖片

下面給出一個例子:

首先創建動作類:MyValidationAction.java

 1 package action;
 2 
 3 import com.opensymphony.xwork2.ActionSupport;
 4 
 5 public class MyValidationAction extends ActionSupport {
 6     
 7     private
String name; 8 private int age; 9 /** 10 * @return the name 11 */ 12 public String getName() { 13 return name; 14 } 15 /** 16 * @param name the name to set 17 */ 18 public void setName(String name) { 19 this.name = name; 20 } 21 /** 22 * @return the age 23 */ 24 public int getAge() { 25 return age; 26 } 27 /** 28 * @param age the age to set 29 */ 30 public void setAge(int age) { 31 this.age = age; 32 } 33 34 /* (non-Javadoc) 35 * @see com.opensymphony.xwork2.ActionSupport#validate() 36 */ 37 38 public void validateTest02() { 39 if(name==null || name.trim().equals("") || name.length()==0){ 40 addFieldError("name","validateTest02:請輸入有效的用戶名"); 41 } 42 43 if(age>120 || age<=0){ 44 addFieldError("age","validateTest02:請輸入有效的年齡"); 45 } 46 47 } 48 49 public void validate() { 50 if(name==null || name.trim().equals("") || name.length()==0){ 51 addFieldError("name","validate:請輸入有效的用戶名"); 52 } 53 54 if(age>120 || age<=0){ 55 addFieldError("age","validate:請輸入有效的年齡"); 56 } 57 58 } 59 60 public String test01(){ 61 System.out.println("test01....."); 62 //System.out.println("user info:"+name+","+age); 63 return "success"; 64 } 65 66 public String test02(){ 67 System.out.println("test02....."); 68 //System.out.println("user info:"+name+","+age); 69 return "success"; 70 } 71 }

書寫表單界面:login8.jsp

 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8"%>
 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 4 <html>
 5 <head>
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <title>登錄實例</title>
 8 </head>
 9 <body>
10 <form action = "Login8_test01" method="post">
11     <p>用戶名 <input type = "text" name="name"/></p>
12     <p>密碼 <input type = "text" name="age"/></p>
13     <input type = "submit" value = "登錄"/>
14 </form>
15 </body>
16 </html>

struts.xml配置:請看這個action, 多出了一個返回result:input

1         <action name="Login8_*" class="action.MyValidationAction" method="{1}">
2             <result name="success">/index.jsp</result>  
3             <result name="input">/error.jsp</result>       
4         </action>

創建MyValidationAction繼承ActionSupport,通過重寫validate()方法實現輸入校驗,validate()方法會校驗action中所有的方法。當某個數據校驗失敗時,我們可以調用addFieldError()方法往系統的fieldErrors添加校驗失敗信息。如果系統的fieldErrors包含失敗信息,sturts2會將請求轉發到名為input的result。在input視圖中,可以通過 <s:fielderror/>標簽顯示失敗信息。若只想對action中的指定方法進行校驗,只需將MyValidationAction中的validate()方法名稱改成validateXxx()即可,其中Xxx為對應的方法名稱,Xxx的第一個字母要大寫。如本例中,若只想對MyValidationAction中的test02()方法進行校驗,則可以將validate()方法名稱改為validateTest02()即可。
加入了某個函數校驗或所有函數校驗方法,實際上就是在運行函數之前,先運行校驗方法。
例如本利中,在瀏覽器輸入:http://localhost:8080/Struts2Demo/login8.jsp

技術分享圖片技術分享圖片

不輸入用戶名和密碼,直接點擊登錄,可以看到就會調用校驗函數Validate()! 發現name和age都校驗不通過。就會通過函數addFieldError("xxx","yyy")將錯誤信息存起來,等回到頁面在顯示出來。通過下面流程圖可以看出,如果filedError中有錯誤信息,workflow攔截器會工作,直接返回input,就會跳轉到input結果碼對應的界面。

技術分享圖片

我們把表頁面簡單修改下:讓它調用test02()函數。

1 <form action = "Login8_test02" method="post">
2     <p>用戶名 <input type = "text" name="name"/></p>
3     <p>密碼 <input type = "text" name="age"/></p>
4     <input type = "submit" value = "登錄"/>
5 </form>

同樣在瀏覽器輸入:http://localhost:8080/Struts2Demo/login8.jsp,然後不輸入用戶名,密碼直接登錄,

技術分享圖片

可以看到此時,action中的兩個校驗函數都進行調用。和前面說明一樣,如果只想對某個函數進行校驗,就在校驗函數後面加上函數名字。

validate()方法會校驗action中所有的方法。若只想對action中的指定方法進行校驗,只需將MyValidationAction中的validate()方法名稱改成validateXxx()即可,其中Xxx為對應的方法名稱,Xxx的第一個字母要大寫。若都存在時,系統通過反射技術先調用action中的validateXxx()方法,然後再調用action中的validate()方法。

總結:輸入校驗的流程。
a. 類型轉換器對請求參數執行類型轉換,並把轉換後的值賦給action中的屬性。
b. 如果在執行類型轉換的過程中出現異常,系統會將異常信息保存到ActionContext,conversionError攔截器將異常信息封裝到fieldErrors裏。不管類型轉換是否出現異常,都會進入第c步。
c. 系統通過反射技術先調用action中的validateXxx()方法,Xxx為方法名。
d. 再調用action中的validate()方法。
e. 經過上面4步,如果系統中的fieldErrors存在錯誤信息(即存放錯誤信息的集合的size大於0),系統自動將請求轉發至名稱為input的視圖。如果系統中的fieldErrors沒有任何信息,系統將執行action中的處理方法。若沒有書寫input結果碼對應的表單界面,將會出現404錯誤。

1.2 xml配置檢驗

實際上就是調用struts2已經定義好的各種校驗器,來對我們書寫的動作類進行校驗。

要求:

1、必須實現validateable接口,actionsupport已經實現了,所以我們只需要直接繼承actionsupport即可 

2、action中必須為屬性提供getXXX、setXXX方法,因為代碼校驗是在Action本類中來完成校驗,這說明我們可以直接使用本類的private屬性,但如果使用XML配置方式校驗,這需要使用校驗框架的代碼來完成校驗工作,那麽校驗框架需要調用Action的getXXX()方法來獲取被校驗的屬性,所以一定要為被校驗的屬性提供getXXX()方法。

創建校驗配置文件

命名規範:

actionClass-actionName-validation.xml  

actionClass:action的類名

actionName:action的訪問名稱,及在struts.xml中配置的,<action name="">, 若有動態函數傳遞,只想對某個函數進行校驗,就需要把函數名顯式寫出來。

validation.xml:固定後綴名。

比如:MyValidationAction2-Login9_test01-validation.xml 這種是對特定方法進行校驗

路徑:必須與action同包下

技術分享圖片

下面給出我們的動作類:MyValidatorAction2.java

 1 package action;
 2 
 3 public class MyValidationAction2 {
 4     
 5     private String name;
 6     private int age;
 7     /**
 8      * @return the name
 9      */
10     public String getName() {
11         return name;
12     }
13     /**
14      * @param name the name to set
15      */
16     public void setName(String name) {
17         this.name = name;
18     }
19     /**
20      * @return the age
21      */
22     public int getAge() {
23         return age;
24     }
25     /**
26      * @param age the age to set
27      */
28     public void setAge(int age) {
29         this.age = age;
30     }
31     
32     public String test01(){
33         System.out.println("test01.....");    
34         //System.out.println("user info:"+name+","+age);        
35         return "success";
36     }
37 
38 }

然後給出我們的登錄表單:login9.jsp

 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8"%>
 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 4 <html>
 5 <head>
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <title>登錄實例</title>
 8 </head>
 9 <body>
10 <form action = "Login9_test01" method="post">
11     <p>用戶名 <input type = "text" name="name"/></p>
12     <p>密碼 <input type = "text" name="age"/></p>
13     <input type = "submit" value = "登錄"/>
14 </form>
15 </body>
16 </html>

給出struts.xml配置:

1         <action name="Login9_*" class="action.MyValidationAction2" method="{1}">
2             <result name="success">/index.jsp</result>  
3             <result name="input">/error.jsp</result>       
4         </action>

下面給出最主要的validator配置:MyValidationAction2-Login9_test01-validation.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2   <!DOCTYPE validators PUBLIC 
 3           "-//Apache Struts//XWork Validator 1.0.2//EN"
 4           "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
 5 
 6 <validators>
 7 
 8     <field name ="name"> <!--action中需要校驗的屬性 -->
 9         <field-validator type="requiredstring">    <!-- 指定校驗器 -->
10             <param name="trim"> true</param>       <!-- 為校驗器中的屬性trim註值,trim默認值-->
11             <message>用戶名不能為空</message>       <!-- 校驗失敗後的提示信息 -->    
12         </field-validator>
13     </field>
14 
15     <field name="age">                    <!-- action中需要校驗的屬性 -->
16         <field-validator type="int">      <!-- 指定校驗器類型 -->
17             <param name="min">0</param>   <!-- 為校驗器中參數配置 -->
18             <param name="max">120</param> <!-- 為校驗器中參數配置 -->
19             <message>年齡輸入不對</message>      <!-- 校驗失敗後輸出錯誤提示信息 -->
20         </field-validator>
21         
22         <field-validator type="required">  
23             <message>請輸入年齡</message>    
24         </field-validator>
25     </field>
26 
27 </validators>

主要是書寫這個xml配置。註意各個字段配置。

下面在瀏覽器輸入:http://localhost:8080/Struts2Demo/login9.jsp

技術分享圖片技術分享圖片

不輸入任何用戶名等,直接點擊登錄,如右圖所以,顯示成功。

看控制臺輸出:

16:08:10.208 [http-bio-8080-exec-15] ERROR action.MyValidationAction2 - Validation error for name:用戶名不能為空
test01.....

由控制臺輸出可以看到,

1:校驗中的錯誤提示信息顯示在了控制臺,並沒有回顯到表單界面。也就是沒有像上面那樣返回碼input。

2:並且雖然校驗不過,依然把函數運行完了,我們看到輸出了“test01.....”

要是這樣就感覺沒什麽用????

通過找資料發現,通過xml配置校驗應該和上面一樣,也會返回結果碼input,並將錯誤信息回顯在表單。哪兒錯了呢???

查看code才發現,是我的action沒有繼承ActionSupport這個父類。導致功能不全。

然後我們action加上父類,public class MyValidationAction2 extends ActionSupport {}

同樣按照上面運行,在瀏覽器就會輸出下面信息,而控制臺就不會輸出任何信息。

技術分享圖片

所以兩種配置方法一樣。

校驗規則有很多,在xwork-core-xxx.jar/com.opensymphony.xwork2/validator/validators/default.xml中就能夠找到所有的校驗規則。

技術分享圖片

技術分享圖片

總結:如果讓我自己選的話,肯定是選擇xml配置校驗的方法,因為,能使用struts2中的一些校驗規則,就無需自己編寫了,不過到後面應該都有其他更方便的校驗方法,而不會使用struts2內置的這些校驗。

二:攔截器

可以說攔截器是struts2的最主要部分之一。struts2也提供了很多攔截器。大家不要以為攔截器就是用來攔截的,其實他幫助我們解決了很多工作。

學到現在,其實我們已經用了很多個攔截器:

參數靜態封裝,我們用了staticparam

參數動態封裝,我們用param,modelDriven

自動類型轉換,我們用convert

上面講述的校驗器,我們也用了攔截器validation.

所以攔截器不是用來攔截的。

系統提供的攔截器

現在應該都知道了,前面說表單提交參數自動封裝時就提到了好幾種攔截器,而上面說校驗數據也提到了兩種攔截器,基本上我們也知道攔截器的作用是啥了,就是在到達action之前做的很多處理,提前幫我們做事情的一種機制,而我們並不需要編寫這些攔截器,因為struts2已經幫我們寫好了常用的一些攔截器,並且有個defaultStack的攔截器棧,我們使用的action就經過struts2提供的這個默認攔截器棧。其中有18個,也就是說,如果不修改默認攔截器棧,那麽每次我們訪問action,都會經過這18個攔截器棧,我們來看看哪18個,struts2的默認攔截器棧(18個攔截器):

技術分享圖片

找到default.stack

技術分享圖片

其中我們應該了解很多個了,277行,i18n用來做國際化,281行,modeDriven用來數據封裝的,282行fileUpload,上傳下載的,285行staticParams用來獲取靜態參數的,287行params用做數據封裝的,290行conversionError標識數據類型轉換異常處理的,291行,validation用來做輸入校驗的 292行workflow用來檢測<filederror>是否有值,有值則跳到input結果碼對應的頁面。 其他的還沒講到到後面我都會一一講解清楚的,先大概了解一下。

自定義攔截器

大多數功能的攔截器struts2都已經幫我們寫好了,但是有一些,我們需要自己在往其中功能,那就必須自定義攔截器了。自定義攔截器很簡單,就分兩步即可。

第一步:編寫攔截器類MyInterceptor,繼承AbstractInterceptor類。(它幫我們實現了Interceptor接口)。

 1 package action;
 2 
 3 import com.opensymphony.xwork2.ActionInvocation;
 4 import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
 5 
 6 public class MyInterceptor extends AbstractInterceptor {
 7 
 8     @Override
 9     public String intercept(ActionInvocation invocation) throws Exception {
10         
11         System.out.println("My interceptor......");
12         return null;
13     }
14     
15     
16     public String test01(){
17         
18         System.out.println("test01.....");
19         
20         return "success";
21     }
22 
23 }

第二步:註冊攔截器,在struts.xml中註冊

在<package>聲明攔截器

在<action>中引用攔截器

直接在上面例子中進行修改,給package配置這個攔截器。struts.xml配置如下

 1     <package name="zsy" namespace="/" extends="struts-default">
 2     
 3         <interceptors>
 4             <interceptor name = "MyInterceptor" class ="action.MyInterceptor"></interceptor>
 5         </interceptors>
 6         
 7         <action name="Login9_*" class="action.MyValidationAction2" method="{1}">
 8             <result name="input">/error.jsp</result>  
 9             <result name="success">/index.jsp</result>     
10             <interceptor-ref name="MyInterceptor"></interceptor-ref>              
11         </action>      
12         
13     </package>

但是一般不用這種,因為Struts2有這麽一種機制,一旦為Action指定了攔截器,那麽就不會再為這個Action執行默認攔截器了,即defaultStack這個攔截器棧中的攔截器都不會執行,也就是說,這個Action沒有輸入校驗、沒有參數註入、沒有國際化、沒有…,這是不行的,

如果我們此時在瀏覽器輸入:http://localhost:8080/Struts2Demo/login9.jsp

瀏覽器不會有任何輸入,在控制臺只輸出:My interceptor......

所以這樣肯定是不可以的。所以我們需要在這個<action>元素中再引用defaultStack攔截器棧。

修改struts.xml如下:

 1     <package name="zsy" namespace="/" extends="struts-default">
 2     
 3         <!-- 申明攔截器 -->
 4         <interceptors>
 5             <interceptor name = "MyInterceptor" class ="action.MyInterceptor"></interceptor>
 6         </interceptors>
 7         
 8         <action name="Login9_*" class="action.MyValidationAction2" method="{1}">
 9             <result name="input">/error.jsp</result>  
10             <result name="success">/index.jsp</result> 
11             <!-- 引用默認攔截器棧 -->
12             <interceptor-ref name="defaultStack"></interceptor-ref>
13             <!-- 使用自定義的攔截器 -->    
14             <interceptor-ref name="MyInterceptor"></interceptor-ref>                 
15                     
16         </action>      
17         
18     </package>

先調用默認攔截器棧,然後調用自定義攔截器,在瀏覽器輸入:http://localhost:8080/Struts2Demo/login9.jsp

瀏覽器輸出:證明默認攔截器生效了。技術分享圖片

但是我們自定義加的攔截器沒有運行,在控制臺沒有任何輸出。

然後我們把定義的順序在struts.xml修改下,將自定義攔截器放在前面,默認攔截器放在後面,如下:

1  <!-- 使用自定義的攔截器 -->    
2             <interceptor-ref name="MyInterceptor"></interceptor-ref> 
3             <!-- 引用默認攔截器棧 -->
4             <interceptor-ref name="defaultStack"></interceptor-ref> 

同樣進行上面測試,瀏覽器不會有任何輸入,在控制臺只輸出:My interceptor......

也就是自定義攔截器運行了,可是默認攔截器沒有運行。???????其實是前面一個地方有錯誤,請看下面解決辦法。

所以這種方案也不行,並且因為只有一個action,如果有十幾個action呢?需要為每個action配置默認攔截器棧和自定義攔截器,也很麻煩。

創建一個攔截器棧,將默認攔截器棧和自定義攔截器加入其中,然後將struts2的默認攔截器棧修改為我們新構建的攔截器棧。

看struts.xml配置:

 1  <package name="zsy" namespace="/" extends="struts-default">
 2     
 3         <!-- 申明攔截器 -->
 4         <interceptors>
 5             <interceptor name = "MyInterceptor" class ="action.MyInterceptor"></interceptor>
 6             <!-- 創建新的攔截器棧 -->
 7             <interceptor-stack name ="myStack">
 8                 <interceptor-ref name="defaultStack"></interceptor-ref>  
 9                 <interceptor-ref name="MyInterceptor"></interceptor-ref> 
10                             
11             </interceptor-stack>
12         </interceptors>
13         <!-- 將默認攔截器棧修改為使用我們自定義的攔截器棧 -->
14         <default-interceptor-ref name = "myStack"></default-interceptor-ref>
15         
16         <action name="Login9_*" class="action.MyValidationAction2" method="{1}">
17             <result name="input">/error.jsp</result>  
18             <result name="success">/index.jsp</result>
19             
20             <!--  <interceptor-ref name="myStack"></interceptor-ref> -->    
21                        
22                     
23         </action>      
24         
25     </package>
 <default-interceptor-ref name = "myStack"></default-interceptor-ref>這個是整個package中所有action都用這個重新定義的攔截器棧。
<!--  <interceptor-ref name="myStack"></interceptor-ref> --> 也可以配置這個,在action中配置,只是這個action用自己新定義的攔截器棧。其他action隨便。

同樣進行上面的測試,發現默認攔截器運行了,自定義攔截器沒有運行。那麽問題出在哪兒了?不可能只能運行一個啊。

下面查出原因是因為我們自定義的攔截器類書寫錯誤,沒有加入遞歸調用String invoke = invocation.invoke();

想想,我們的攔截器是怎麽一次運行起來的,就是一個個相互遞歸調用。所以修改後的自定義攔截器如下:

 1 package action;
 2 
 3 import com.opensymphony.xwork2.ActionInvocation;
 4 import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
 5 
 6 public class MyInterceptor extends AbstractInterceptor {
 7 
 8     @Override
 9     public String intercept(ActionInvocation invocation) throws Exception {
10         
11         System.out.println("My interceptor......");
12         String invoke = invocation.invoke();
13 
14         return invoke;
15         
16     }
17     
18     
19     public String test01(){
20         
21         System.out.println("test01.....");
22         
23         return "success";
24     }
25 
26 }

然後在進行測試,無論自定義攔截器放前面還是後面,都可以被調用了。

到此,攔截器我們就可以自己定義了。

了解了struts2中數據校驗的功能和struts2中的18個攔截器,還有如何自定義攔截器這些操作,個人感覺還是沒有難度的,現在只是在學習知識,學會這個知識點,等後面使用struts2來寫一個小的demo,就會將所有零碎的知識點整合到一起。好好努力。

主要參考博客:

https://www.cnblogs.com/whgk/p/6593916.html-------寫的非常好。

Struts2各個功能詳解(2)-輸入校驗和攔截器