勿以恶小而为之,勿以善小而不为--------------------------刘备
劝诸君,多行善事积福报,莫作恶
上一章简单介绍了Struts2编写自定义验证拦截敏感词汇(十二),如果没有看过,请观看上一章
一.为什么要进行表单验证
在实际开发和生活中,当我们在一个表单填写好数据,进行提交时,如果这个时候网卡,我们一般会再次,甚至是多次点击提交按钮(以为这样会快),也有的会先将浏览的页面返回到上一步,再进行退回,或者重复点击回车, 这样会导致数据重复提交。
为了防止表单重复提交,可以在客户端进行简单的处理,可以在服务器端进行处理。
这里只说明在服务器端进行的处理。
如果想了解客户端处理的,可以观看孤傲苍狼前辈的博客
二 搭建Struts2的基本运行环境,演示重复提交
二.一 创建UserAction
里面有常用的五个方法。
package com.yjl.web.action;
import org.apache.log4j.Logger;
import com.opensymphony.xwork2.ActionSupport;
/**
* @author 两个蝴蝶飞
* @version 创建时间:2018年9月14日 下午6:35:10
* 演示重复提交的Action.在添加用户是进行演示
*/
public class UserAction extends ActionSupport {
private static Logger logger=Logger.getLogger(UserAction.class);
private static final long serialVersionUID = -4164832837385401186L;
public String toAddUI(){
logger.info("跳转到添加学生的页面");
return "addUI";
}
public String add(){
logger.info("执行添加学生的操作");
return "toList";
}
public String toEditUI(){
logger.info("跳转到添加学生的页面");
return "editUI";
}
public String edit(){
logger.info("执行修改学生的操作");
return "toList";
}
public String delete(){
logger.info("执行删除学生的操作");
return "toList";
}
public String list(){
logger.info("执行查询学生的操作");
return "list";
}
}
二.二 配置struts.xml文件
<package name="user" namespace="/" extends="struts-default">
<action name="User_*" class="com.yjl.web.action.UserAction" method="{1}">
<result name="addUI">/WEB-INF/content/add.jsp</result>
<result name="editUI">/WEB-INF/content/edit.jsp</result>
<result name="list">/WEB-INF/content/list.jsp</result>
<!-- 应该当添加成功之后,就跳转到List界面 -->
<result name="toList" type="chain">User_list</result>
</action>
</package>
二.三 编写前端页面
二.三.一 编写 /content/add.jsp 页面
<body>
<h3>这是一个添加学生的页面</h3>
<s:form action="User_add" method="post" namespace="/">
<s:textfield label="姓名" name="name"></s:textfield>
<s:submit value="添加学生"/>
</s:form>
</body>
二.三.二 编写 /content/edit.jsp 页面
<body>
<h3>这是一个修改学生的页面</h3>
<s:form action="User_edit" method="post" namespace="/">
<s:textfield label="姓名" name="name"></s:textfield>
<s:submit value="添加学生"/>
</s:form>
</body>
二.三.三 编写 /content/list.jsp 页面
<body>
<h3>这是一个显示学生的页面</h3>
</body>
二.四 重启服务器,运行程序
输入网址: http://localhost:8080/Struts_Token/User_toAddUI
当输入名字之后 ,
点击添加学生之后:
日志栏:
页面上会显示:
下面会显示学生的信息,
注意此时上面的地址栏是:User_add.action。
二.五 出现的问题
- 此时如果用户将光标定位到地址栏, 继续点击回车的话,还会继续执行一遍add()的方法:
2.如果用户刷新浏览器的话,也会继续执行一遍add()的方法:
会发现根本原因主要是地址栏的原因。地址栏的地址仍然是 User_add.action, 而不是 User_list.action
需要在 struts.xml中配置时type类型的值。
二.六 将类型type改成redirectAction
将toList 的返回类型,由默认的 chain改成redirectAction。
<!--错误的用法:<result name="toList" type="chain">User_list</result>-->
<result name="toList" type="redirectAction">User_list</result>
二.七 修改后重新验证
点击添加学生按钮:
再次点击回车按钮时:
刷新浏览器时:
都只是执行list()的方法,并不会再次执行add()的方法。
三. 方法运行时间过长导致的问题
你以为只是改成一个type值就万事大吉了吗?你太天真了。
如果添加add()这个方法运行的时候够长的话,仍然会有一些错误的。
实际情况中,add()这个方法会执行很多的逻辑验证,并不是只改变一个表,所以运行时间可能会长。
用Thread线程的睡眠来模拟这种情况。
三.一 在 add() 方法中,添加延迟处理
在add()方法中添加一个休眠的处理:
public String add(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("执行添加学生的操作");
return "toList";
}
三.二 重启服务器,再次验证
重启服务器,进行相应的验证:
点击一次添加后,浏览器在转圈,用户非常有可能再点一次,那么就会产生这种情况:
很明显,执行了两次添加学生的操作。 这样,在展示数据的时候,就会展示两条, 而用户明明只添加了一条。
这是非常不可行的。
以前由于添加时时间短,没有发现这个问题。现在发现了,必须要去除。
Struts2框架提供了拦截器,来避免这一点
四. 利用 tokenSession 拦截器 防止表单提交
这种方法是在用户要提交的表单中,加入一个<s:token>标签,这样,当浏览器第一次访问这个带有<s:token>标签的页面时,
在服务器中,解析<s:token>标签的类(TokenTag.class),会生成一个随机的字符串(这个字符串,查看网页的源代码可以看到),
并且发送给客户端的浏览器,同时,在服务器中,会把这个随机字符串保存到用户的session对象中。
当第一次提交表单时,在服务器中,会比较客户端和服务器中分别保存的这个随机字符串,因为是第一次提交,所以这两个字符串相等,
然后进行正常的业务处理。第一次提交后,在服务器中的session中保存的这个随机字符串,会改变为其他的随机值,注意,这是很重要的一步!
此时,地址栏停留在处理用户提交数据的Action中,客户端中保存的随机字符串没有改变,若是刷新页面,
即重复提交,服务器再进行两个字符串的比较,会不相等,就会跳转到name为invalid.token的结果页面中,这样就会防止表单重复提交了。
(摘录于hackerain前辈的博客:javascript:void(0))
四.一 在 add.jsp 表单中添加<s:token/>
在add.jsp页面添加<s:token></s:token>
<body>
<h3>这是一个添加学生的页面</h3>
<s:form actinotallow="User_add" method="post" namespace="/">
<!-- 添加一个token -->
<s:token></s:token>
<s:textfield label="姓名" name="name"></s:textfield>
<s:submit value="添加学生"/>
</s:form>
</body>
Struts2中已经实际上token的操作,将其转成了一个拦截器。在package包下引用这个拦截器即可。
四.二 配置struts.xml文件
<package name="user" namespace="/" extends="struts-default">
<action name="User_*" class="com.yjl.web.action.UserAction" method="{1}">
<interceptor-ref name="tokenSession">
<!-- token对哪些方法起作用 -->
<param name="includeMethods">add,edit,delete</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<result name="addUI">/WEB-INF/content/add.jsp</result>
<result name="editUI">/WEB-INF/content/edit.jsp</result>
<result name="list">/WEB-INF/content/list.jsp</result>
<!-- 应该当添加成功之后,就跳转到List界面 -->
<result name="toList" type="redirectAction">User_list</result>
</action>
</package>
四.三 重启服务器,进行验证Token
输入网址: http://localhost:8080/Struts_Token/User_list.action
输入姓名后, 正常点击一次添加学生的按钮:
发现正常的跳转和使用。
点击两次或者多次添加学生的按钮时:
添加学生的操作也只添加了一次。
进行修改的话,多次点击,也是只修改一次。
完成正常的逻辑和功能操作。
本章节的代码链接为:
链接:https://pan.baidu.com/s/1lo58hZkgq9K0uyyMZLnGmg
提取码:09hl
谢谢您的观看!!!