码迷,mamicode.com
首页 > 其他好文 > 详细

struts2(三) 输入校验和拦截器

时间:2020-03-02 20:27:42      阅读:83      评论:0      收藏:0      [点我收藏+]

标签:创建   参数   错误信息   static   abstract   自己   类型   return   拒绝   

前面知道了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个拦截器):

技术图片

找到defaultStack

技术图片

其中我们应该了解很多个了,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-------写的非常好。

 

转载自https://www.cnblogs.com/beilou310/p/10616006.html

struts2(三) 输入校验和拦截器

标签:创建   参数   错误信息   static   abstract   自己   类型   return   拒绝   

原文地址:https://www.cnblogs.com/swff/p/12397605.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!