Struts2各个功能详解(2)-输入校验和拦截器
前面知道了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)-输入校验和拦截器的更多相关文章
- struts2(三) 输入校验和拦截器
前面知道了struts2的架构图和struts2的自动封装表单参数和数据类型自动转换,今天来学struts2的第三第四个东西,输入校验和拦截器, --WH 一.输入校验 在以前我们写一个登录页面时,并 ...
- Struts2学习(三)———— 输入校验和拦截器
一.输入校验 在以前我们写一个登录页面时,并没有限制用户的输入,不管用户输入什么,我们都存入数据库中,很显然这是不行的,我们需要检测用户输入的文本是否合法,是否符合我们需要的文本格式,符合菜放行,而s ...
- struts2各个功能详解(1)----参数自动封装和类型自动转换
struts2里面的各个功能,现在确实都不清楚,完全属于新学! 通过前面的例子,有时就会疑问,这些jsp中的数据信息是怎么传送给action的?例如: <form action = " ...
- 第15.44节、PyQt输入部件:QAbstractSlider派生类QScrollBar滚动条、QSlider滑动条、QDial刻度盘功能详解
专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 一.引言 Designer中的输入部件Horizo ...
- 第15.42节、PyQt输入部件:QFontComboBox、QLineEdit、QTextEdit、QPlainText功能详解
专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 一.引言 输入部件量比较多,且功能很丰富,但除了用于编写编辑器.浏览器 ...
- 第三十五章、PyQt输入部件:QFontComboBox、QLineEdit、QTextEdit、QPlainText功能详解
专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 一.引言 输入部件量比较多,且功能很丰富,但除了用于编写编辑器.浏览器 ...
- 第15.41节、PyQt(Python+Qt)入门学习:输入部件QComboBox组合框功能详解
专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 一.概述 Designer中输入工具部件中的Combo Box组合框与 ...
- SVN功能详解
SVN功能详解 TortoiseSVN是windows下其中一个非常优秀的SVN客户端工具.通过使用它,我们可以可视化的管理我们的版本库.不过由于它只是一个客户端,所以它不能对版本库进行权限管理. ...
- SNS社交系统“ThinkSNS V4.6”活动应用功能详解及应用场景举例
sns社交系统ThinkSNS目前拥有功能:朋友圈(微博).微吧(论坛).频道.积分商城.IM即时聊天.直播.问答.活动.资讯(CMS).商城.广场.找人.搜索.评论.点赞.转发.分享.话题.积分.充 ...
随机推荐
- 架构师技能图谱 V1.2
系统架构能力 基本理论 扩展性设计 可用性设计 可靠性设计 一致性设计 负载均衡设计 过载保护设计 灾难恢复和备份 协议设计 二进制协议 文本协议 接入层架构设计 DNS 轮询 动静态分离 静态化 反 ...
- 更多的bash命令
深入介绍Linux系统管理命令,可以使用这些命令处理系统上的数据文件: 1.监控程序 1.1.进程的查看 ps 查看进程 GNU ps 命令支持3种不同的命令行参数: 1.Unix类型参数,前面一条短 ...
- hive 表新增字段后更新分区无法显示数据
解决方案: 1.删除分区后重新跑数据 alter table drop partition(分区字段=“”): 2.新增字段运行程序后其实数据已经有了,只是查询hive的时候无法显示出来, 这个时候只 ...
- Android几种视频播放方式,VideoView、SurfaceView+MediaPlayer、TextureView+MediaPlayer,以及主流视频播放器开源项目
简单的说下一Android的几种视频播放功能: 1.VideoView:最简单的视频播放 <FrameLayout xmlns:android="http://schemas.andr ...
- Hexo NexT主题/cnblog等博客增加点击出现红心的特效
查看效果 每日前端 我的博客 实现:1.下载这段js代码到项目目录 ===>下载地址2.在需要的地方添加js代码 <script type="text/javascript&qu ...
- 字节码操作JAVAssist
字节码操作Javassist 字节码:字节码是设计被用来将代码高效的传送给多种软件平台.硬件平台,字节码的设计也实现了Java的平台无关性,字节码比机器码更抽象,它通常被认为是包含了一个可执行文件的二 ...
- VS编写一个项目的路径规划
原文路径:http://blog.csdn.net/puttytree/article/details/7838419 https://www.cnblogs.com/zhehan54/p/45678 ...
- idea搭建ssm框架
1.file-->new-->project-->maven.... 2.建立后的目录: 3.pom.xml依赖建立: <?xml version="1.0" ...
- Java学习随笔(2)--爬虫--天气预报
public class Spiderweather { public static void main(String[] args) { List<String> list = null ...
- mysql监控执行的sql语句
转载 https://blog.csdn.net/nzjdsds/article/details/77513869 MySQL用SQL开启general_log并设置路径 2017年08月24日 00 ...