前面知道了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-------写的非常好。