前面知道了struts2的架构图和struts2的自动封装表单参数和数据类型自动转换,今天来学struts2的第三第四个东西,输入校验和拦截器。

 一:输入校验

客户端校验进行基本校验,如检验非空字段是否为空,数字格式是否正确等。客户端校验主要用来过滤用户的误操作。作用是:拒绝误操作输入提交到服务器处理,降低服务器端负担。
  服务器端校验也必不可少,服务器端校验防止非法数据进去程序,导致程序异常,底层数据库异常。服务器端校验是保证程序有效进行及数据完整的手段.
对异常输入的过滤,就是输入校验,也称为数据校验
输入校验分为客户端校验和服务器校验:
1. 客户端校验主要是过滤正常用户的误操作,主要通过js代码完成。

2. 服务器端校验是整个应用阻止非法数据的最后防线,主要通过在应用中编程实现

我们这个地方学习的是服务器端校验。

在以前我们写一个登录页面时,并没有限制用户的输入,不管用户输入什么,我们都存入数据库中,很显然这是不行的,我们需要检测用户输入的文本是否合法,是否符合我们需要的文本格式,符合才放行,而struts2中就有这种功能,能帮我们在服务器段进行判断,比如用户名不能为空,年龄只能在0-100之间等。现在我们就来说说如何使用struts2中的校验功能。分为两种,编程式校验和配置校验(XML配置校验)

验证器的验证时机:
验证发生在execute方法之前,在struts2 的params拦截器已经把请求的参数通过反射设置到 Action 的属性之后,所以,验证框架实际上验证的是值栈中的值

验证的结果:
如果用户输入的参数完全满足验证结果,那么会继续执行execute方法。如果不满足,会跳转到Action配置中的result name="input" 的页面中中

1.1 编程式校验

下面给出一个例子:

首先创建动作类:MyValidationAction.java

 package action;

 import com.opensymphony.xwork2.ActionSupport;

 public class MyValidationAction extends ActionSupport {

     private String name;
private int age;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
} /* (non-Javadoc)
* @see com.opensymphony.xwork2.ActionSupport#validate()
*/ public void validateTest02() {
if(name==null || name.trim().equals("") || name.length()==0){
addFieldError("name","validateTest02:请输入有效的用户名");
} if(age>120 || age<=0){
addFieldError("age","validateTest02:请输入有效的年龄");
} } public void validate() {
if(name==null || name.trim().equals("") || name.length()==0){
addFieldError("name","validate:请输入有效的用户名");
} if(age>120 || age<=0){
addFieldError("age","validate:请输入有效的年龄");
} } public String test01(){
System.out.println("test01.....");
//System.out.println("user info:"+name+","+age);
return "success";
} public String test02(){
System.out.println("test02.....");
//System.out.println("user info:"+name+","+age);
return "success";
}
}

书写表单界面:login8.jsp

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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>登录实例</title>
</head>
<body>
<form action = "Login8_test01" method="post">
<p>用户名 <input type = "text" name="name"/></p>
<p>密码 <input type = "text" name="age"/></p>
<input type = "submit" value = "登录"/>
</form>
</body>
</html>

struts.xml配置:请看这个action, 多出了一个返回result:input

         <action name="Login8_*" class="action.MyValidationAction" method="{1}">
<result name="success">/index.jsp</result>
<result name="input">/error.jsp</result>
</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()函数。

 <form action = "Login8_test02" method="post">
<p>用户名 <input type = "text" name="name"/></p>
<p>密码 <input type = "text" name="age"/></p>
<input type = "submit" value = "登录"/>
</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

 package action;

 public class MyValidationAction2 {

     private String name;
private int age;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
} public String test01(){
System.out.println("test01.....");
//System.out.println("user info:"+name+","+age);
return "success";
} }

然后给出我们的登录表单:login9.jsp

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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>登录实例</title>
</head>
<body>
<form action = "Login9_test01" method="post">
<p>用户名 <input type = "text" name="name"/></p>
<p>密码 <input type = "text" name="age"/></p>
<input type = "submit" value = "登录"/>
</form>
</body>
</html>

给出struts.xml配置:

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

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

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd"> <validators> <field name ="name"> <!--action中需要校验的属性 -->
<field-validator type="requiredstring"> <!-- 指定校验器 -->
<param name="trim"> true</param> <!-- 为校验器中的属性trim注值,trim默认值-->
<message>用户名不能为空</message> <!-- 校验失败后的提示信息 -->
</field-validator>
</field> <field name="age"> <!-- action中需要校验的属性 -->
<field-validator type="int"> <!-- 指定校验器类型 -->
<param name="min">0</param> <!-- 为校验器中参数配置 -->
<param name="max">120</param> <!-- 为校验器中参数配置 -->
<message>年龄输入不对</message> <!-- 校验失败后输出错误提示信息 -->
</field-validator> <field-validator type="required">
<message>请输入年龄</message>
</field-validator>
</field> </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接口)。

 package action;

 import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class MyInterceptor extends AbstractInterceptor { @Override
public String intercept(ActionInvocation invocation) throws Exception { System.out.println("My interceptor......");
return null;
} public String test01(){ System.out.println("test01....."); return "success";
} }

第二步:注册拦截器,在struts.xml中注册

在<package>声明拦截器

在<action>中引用拦截器

直接在上面例子中进行修改,给package配置这个拦截器。struts.xml配置如下

     <package name="zsy" namespace="/" extends="struts-default">

         <interceptors>
<interceptor name = "MyInterceptor" class ="action.MyInterceptor"></interceptor>
</interceptors> <action name="Login9_*" class="action.MyValidationAction2" method="{1}">
<result name="input">/error.jsp</result>
<result name="success">/index.jsp</result>
<interceptor-ref name="MyInterceptor"></interceptor-ref>
</action> </package>

但是一般不用这种,因为Struts2有这么一种机制,一旦为Action指定了拦截器,那么就不会再为这个Action执行默认拦截器了,即defaultStack这个拦截器栈中的拦截器都不会执行,也就是说,这个Action没有输入校验、没有参数注入、没有国际化、没有…,这是不行的,

如果我们此时在浏览器输入:http://localhost:8080/Struts2Demo/login9.jsp

浏览器不会有任何输出,在控制台只输出:My interceptor......

所以这样肯定是不可以的。所以我们需要在这个<action>元素中再引用defaultStack拦截器栈。

修改struts.xml如下:

     <package name="zsy" namespace="/" extends="struts-default">

         <!-- 申明拦截器 -->
<interceptors>
<interceptor name = "MyInterceptor" class ="action.MyInterceptor"></interceptor>
</interceptors> <action name="Login9_*" class="action.MyValidationAction2" method="{1}">
<result name="input">/error.jsp</result>
<result name="success">/index.jsp</result>
<!-- 引用默认拦截器栈 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<!-- 使用自定义的拦截器 -->
<interceptor-ref name="MyInterceptor"></interceptor-ref> </action> </package>

先调用默认拦截器栈,然后调用自定义拦截器,在浏览器输入:http://localhost:8080/Struts2Demo/login9.jsp

浏览器输出:证明默认拦截器生效了。

但是我们自定义加的拦截器没有运行,在控制台没有任何输出。

然后我们把定义的顺序在struts.xml修改下,将自定义拦截器放在前面,默认拦截器放在后面,如下:

  <!-- 使用自定义的拦截器 -->
<interceptor-ref name="MyInterceptor"></interceptor-ref>
<!-- 引用默认拦截器栈 -->
<interceptor-ref name="defaultStack"></interceptor-ref>

同样进行上面测试,浏览器不会有任何输入,在控制台只输出:My interceptor......

也就是自定义拦截器运行了,可是默认拦截器没有运行。???????其实是前面一个地方有错误,请看下面解决办法。

所以这种方案也不行,并且因为只有一个action,如果有十几个action呢?需要为每个action配置默认拦截器栈和自定义拦截器,也很麻烦。

创建一个拦截器栈,将默认拦截器栈和自定义拦截器加入其中,然后将struts2的默认拦截器栈修改为我们新构建的拦截器栈。

看struts.xml配置:

  <package name="zsy" namespace="/" extends="struts-default">

         <!-- 申明拦截器 -->
<interceptors>
<interceptor name = "MyInterceptor" class ="action.MyInterceptor"></interceptor>
<!-- 创建新的拦截器栈 -->
<interceptor-stack name ="myStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="MyInterceptor"></interceptor-ref> </interceptor-stack>
</interceptors>
<!-- 将默认拦截器栈修改为使用我们自定义的拦截器栈 -->
<default-interceptor-ref name = "myStack"></default-interceptor-ref> <action name="Login9_*" class="action.MyValidationAction2" method="{1}">
<result name="input">/error.jsp</result>
<result name="success">/index.jsp</result> <!-- <interceptor-ref name="myStack"></interceptor-ref> --> </action> </package>
 <default-interceptor-ref name = "myStack"></default-interceptor-ref>这个是整个package中所有action都用这个重新定义的拦截器栈。
<!--  <interceptor-ref name="myStack"></interceptor-ref> --> 也可以配置这个,在action中配置,只是这个action用自己新定义的拦截器栈。其他action随便。

同样进行上面的测试,发现默认拦截器运行了,自定义拦截器没有运行。那么问题出在哪儿了?不可能只能运行一个啊。

下面查出原因是因为我们自定义的拦截器类书写错误,没有加入递归调用String invoke = invocation.invoke();

想想,我们的拦截器是怎么一次运行起来的,就是一个个相互递归调用。所以修改后的自定义拦截器如下:

 package action;

 import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class MyInterceptor extends AbstractInterceptor { @Override
public String intercept(ActionInvocation invocation) throws Exception { System.out.println("My interceptor......");
String invoke = invocation.invoke(); return invoke; } public String test01(){ System.out.println("test01....."); return "success";
} }

然后在进行测试,无论自定义拦截器放前面还是后面,都可以被调用了。

到此,拦截器我们就可以自己定义了。

了解了struts2中数据校验的功能和struts2中的18个拦截器,还有如何自定义拦截器这些操作,个人感觉还是没有难度的,现在只是在学习知识,学会这个知识点,等后面使用struts2来写一个小的demo,就会将所有零碎的知识点整合到一起。好好努力。

主要参考博客:

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

Struts2各个功能详解(2)-输入校验和拦截器的更多相关文章

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

    前面知道了struts2的架构图和struts2的自动封装表单参数和数据类型自动转换,今天来学struts2的第三第四个东西,输入校验和拦截器, --WH 一.输入校验 在以前我们写一个登录页面时,并 ...

  2. Struts2学习(三)———— 输入校验和拦截器

    一.输入校验 在以前我们写一个登录页面时,并没有限制用户的输入,不管用户输入什么,我们都存入数据库中,很显然这是不行的,我们需要检测用户输入的文本是否合法,是否符合我们需要的文本格式,符合菜放行,而s ...

  3. struts2各个功能详解(1)----参数自动封装和类型自动转换

    struts2里面的各个功能,现在确实都不清楚,完全属于新学! 通过前面的例子,有时就会疑问,这些jsp中的数据信息是怎么传送给action的?例如: <form action = " ...

  4. 第15.44节、PyQt输入部件:QAbstractSlider派生类QScrollBar滚动条、QSlider滑动条、QDial刻度盘功能详解

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 一.引言 Designer中的输入部件Horizo ...

  5. 第15.42节、PyQt输入部件:QFontComboBox、QLineEdit、QTextEdit、QPlainText功能详解

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 一.引言 输入部件量比较多,且功能很丰富,但除了用于编写编辑器.浏览器 ...

  6. 第三十五章、PyQt输入部件:QFontComboBox、QLineEdit、QTextEdit、QPlainText功能详解

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 一.引言 输入部件量比较多,且功能很丰富,但除了用于编写编辑器.浏览器 ...

  7. 第15.41节、PyQt(Python+Qt)入门学习:输入部件QComboBox组合框功能详解

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 一.概述 Designer中输入工具部件中的Combo Box组合框与 ...

  8. SVN功能详解

    SVN功能详解   TortoiseSVN是windows下其中一个非常优秀的SVN客户端工具.通过使用它,我们可以可视化的管理我们的版本库.不过由于它只是一个客户端,所以它不能对版本库进行权限管理. ...

  9. SNS社交系统“ThinkSNS V4.6”活动应用功能详解及应用场景举例

    sns社交系统ThinkSNS目前拥有功能:朋友圈(微博).微吧(论坛).频道.积分商城.IM即时聊天.直播.问答.活动.资讯(CMS).商城.广场.找人.搜索.评论.点赞.转发.分享.话题.积分.充 ...

随机推荐

  1. Best Practices and Recommendations for RAC databases with SGA size over 100GB (文档 ID 1619155.1)

    Best Practices and Recommendations for RAC databases with SGA size over 100GB (文档 ID 1619155.1) APPL ...

  2. 二、Python发展始

    1989年的圣诞节,Guido开始编写Python语言的编译器.Python这个名字,来自Guido所挚爱的电视剧Monty Python’s Flying Circus.他希望这个新的叫做Pytho ...

  3. 从内部入手,浅谈malloc和new的区别

    想要理解一样事物,就要先用自己的语言去描述一件事物.在我查阅资料后,发现malloc函数简单说来就是空闲内存空间收集器,并把空闲空间关联起来,用术语来说就是:将空闲内存块合并起来并称为"闲置 ...

  4. Postman A请求的返回值作为B请求的入参( 之‘’token‘’ ,用代码设置全局变量)

    问题: 登陆接口获取token,其他接口访问时需携带token 方案: 在登陆接口访问后设置Postman的全局变量(Globals),例如设置环境变量名:token2,值(实时的不用自己手动设置的) ...

  5. Python Excel 多sheet 多条数据 自定义写入

    pip install xlwt python excel 数据写入操作,处理网站数据导出以及不是太多数据的爬虫存储, 用处蛮多的轮子. (150+++++++++++++++++++++++++++ ...

  6. windows 查看端口占用、关闭端口

    cmd打卡命令窗口 1)netstat -an 查看所有活动连接 2)netstat -aon| findstr “xxxx” 找到指定端口的连接 3)taskkill  /F /PID xxxx 终 ...

  7. springCloud 服务注册启动报错<com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect>

    报错:com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: ...

  8. Android开发 静态static类与static方法持有Context是否导致内存泄露的疑问

    简述 在Android开发的过程中,难免会使用单例模式或者静态方法工具类.我们会让它们持有一些外部的Context或者View一般有以下几种情况: 单例模式,类的全局变量持有Context 或 Vie ...

  9. Android 开发 ConstraintLayout详解

    implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3' app:layout_constraintHorizo ...

  10. ActiveMQ(4)---ActiveMQ原理分析之消息消费

    消费端消费消息的原理 我们通过上一节课的讲解,知道有两种方法可以接收消息,一种是使用同步阻塞的MessageConsumer#receive方法.另一种是使用消息监听器MessageListener. ...