JSP 重复提交 解决方法
2010-10-15 19:57:35| 分类: STRUTS | 标签:令牌 提交 jsp session request |字号 订阅
----------------------- Page 1-----------------------
看了网上的,有几种方法:
1 在你的表单页里HEAD区加入这段代码:
<META HTTP-EQUIV="pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate">
<META HTTP-EQUIV="expires" CONTENT="Wed, 26 Feb 1997 08:21:57 GMT">
2
生成一个令牌保存在用户session中,在form中加一个hidden域,显示该令
牌的值,form提交后重新生成一个新的令牌,将用户提交的令牌和session
中的令牌比较,如相同则是重复提交
3
在你的服务器端控件的代码中使用Response.Redirect("selfPage")语句。但是
大多的数都不使用这种方法。
方法还有很多。。。
4
<input type="button" value="提交"
onclick="this.disabled=true;this.form.submit()">
5
在JSP页面的FORM表单中添加一个hidden域
<input type="hidden" name="url"value=<%=request.getRequestURL()%>
>
在你的serverlet中添加如下语句
String url=request.getParameter("url");
response.sendRedirect(url);
我一般都是采用这样的方法返回JSP页面的,不太明白你说的重复刷新是什么概
念
6 ajax 无刷新提交
7 Web开发中防止浏览器的刷新键引起系统操作重复提交
怎么解决呢?重定向可以解决页面刷新带来的数据的重复提交的问题,我们自然
可以利用重定向的方式来解决这个问题。但是struts的action里面
mapping.findword();跳转的话,默认的是在工程文件夹里面找要跳转的页面。
这种情况,怎么解决呢?
修改struts-config.xml 文件, 在action里面有一个redirect重新定向的
属性,struts中默认的是false,添加这个属性,改成true,在forword中写
上要跳转页面的绝对或者相对地址就行了
修改如下:
<action-mappings>
<action attribute="newsActionForm" name="newsActionForm"
input="/addnews.jsp" path="/newsAction" parameter="method"
scope="request" type="com.yongtree.news.action.NewsAction">
<forward name="list" path="/listnews.jsp"
----------------------- Page 2-----------------------
redirect="true"></forward>
<forward name="error" path="/addnews.jsp"></forward>
</action>
</action-mappings>
重复提交、重复刷新、防止后退的问题以及处理方式
一。前言
你在任何一个比较专业的BBS都会看到这样的问题,即使你Google一下,也会
发现有很多的人在关注和询问,但大家给出的解决方法却都是千差万别,(有 的
人主张采用脚本来解决;有的则想重定向到别的页面;有的则将此问题提升到
Token的角度)为什么会有如此大的差异呢?
二。问题场景
首先,我们应该先了解为什么要处理这样的问题?或者专业一点就是它适合的场
景是什么?(似乎只有人来问没有人来解释)
1。重复提交、重复刷新的场景
重复提交、重复刷新都是来解决系统重复记录的问题。也就是说某个人在多次的
提交某条记录(为什么?也许是闲了没有事情干的;最有可能是用户根本就不知
道自己的提交结果是否已经执行了?!)。
但出现了这样的问题并不见得就必须处理,要看你所开发的系统的类别而定。比
如你接手的是某个资源管理系统,系统本身从需求的角度根本就不允许出现" 重
复"的记录,在这样需求的约束条件下,去执行重复的提交动作只会引发“业务
级异常”的产生,根本就不可能执行成功也就无所谓避免不避免的问题了。
2。防止后退的场景
了解了重复刷新、重复提交的场景,我们来了解一下"防止后退"操作的原因是什
么?比如你在开发某个投票系统,它有很多的步骤,并且这些步骤之间是有联系
的,比如第一步会将某些信息发送给第二步,第二步缓存了这些信息,同时将自
身的信息发送给了第三步。。。。。等等,如果此时用户处在第三步骤下,我们
想象 一下某个淘气用户的用户点击了后退按钮,此时屏幕出现了第二步骤的页
面,他再次的修改或者再次的提交,进入到下一个步骤(也就是第三步骤),错
误就会在此 产生?!什么错误呢?最为典型的就是这样的操作直接导致了对于
第一个步骤信息的丢失!(如果这样的信息是依靠Request存放的话,当然你可
以存放在 Session或者更大的上下文环境中,但这不是个好主意!关于信息存
放的问题,下次在就这个问题详细的讨论)
三。如何处理的问题
当然很多的系统(比如订票系统从需求上本身是允许个人重复订票的)是必须要
避免重复刷新、重复提交、以及防止后退的问题的,但即使是这样的问题,也要
区分 如何处理以及在哪里处理的(网上只是告诉你如何处理,但很少去区分在
哪里处理的),显然处理的方式无非是客户端或者服务器端两种,而面对不同的
----------------------- Page 3-----------------------
位置处理的 方式也是不同的,但有一点要事先声明:任何客户端(尤其是B/S
端)的处理都是不可信任的,最好的也是最应该的是服务器端的处理方法。
客户端处理:
面对客户端我们可以使用Javascript脚本来解决,如下
1。重复刷新、重复提交
Ways One:设置一个变量,只允许提交一次。
<script language="javascript">
var checkSubmitFlg = false;
function checkSubmit() {
if (checkSubmitFlg true) {
return false;
}
checkSubmitFlg = true;
return true;
}
document.ondblclick = function docondblclick() {
window.event.returnValue = false;
}
document.onclick = function doconclick() {
if (checkSubmitFlg) {
window.event.returnValue = false;
}
}
</script>
<html:form action="myAction.do" method="post" onsubmit="return
checkSubmit();">
Way Two : 将提交按钮或者image置为disable
<html:form action="myAction.do" method="post"
onsubmit="getElById('submitInput').disabled = true; return
true;">
<html:image styleId="submitInput" src="http://datadig.blog.163.com/blog/images/ok_b.gif" border "0" />
</html:form>
2。防止用户后退
这里的方法是千姿百态,有的是更改浏览器的历史纪录的,比如使用
window.history.forward()方法;有的是“用新页面的URL替换当 前的历史纪
录,这样浏览历史记录中就只有一个页面,后退按钮永远不会变为可用。”比如
使用 javascript:location.replace(this.href); event.returnValue=false;
2.服务器端的处理(这里只说Struts框架的处理)
----------------------- Page 4-----------------------
利用同步令牌(Token)机制来解决Web应用中重复提交的问题,Struts也给出
了一个参考实现。
基本原理:
服务器端在处理到达的请求之前,会将请求中包含的令牌值与保存在当前用户会
话中的令牌值进行比较,
看是否匹配。在处理完该请求后,且在答复发送给客户端之前,将会产生一个新
的令牌,该令牌除传给
客户端以外,也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到
刚才的提交页面并再次
提交的话,客户端传过来的令牌就和服务器端的令牌不一致,从而有效地防止了
重复提交的发生。
if (isTokenValid(request, true)) {
// your code here
return mapping.findForward("success");
} else {
saveToken(request);
return mapping.findForward("submitagain");
}
Struts根据用户会话ID和当前系统时间来生成一个唯一(对于每个会话)令牌
的,具体实现可以参考
TokenProcessor类中的generateToken()方法。
1. //验证事务控制令牌,<html:form >会自动根据session中标识生成一个隐含
input代表令牌,防止两次提交
2. 在action中:
//<input type="hidden"
name="org.apache.struts.taglib.html.TOKEN"
// value="6aa35341f25184fd996c4c918255c3ae">
if (!isTokenValid(request))
errors.add(ActionErrors.GLOBAL_ERROR,
new ActionError("error.transaction.token"));
resetToken(request); //删除session中的令牌
3. action有这样的一个方法生成令牌
protected String generateToken(HttpServletRequest request) {
HttpSession session = request.getSession();
try {
byte id[] = session.getId().getBytes();
byte now[] =
new
----------------------- Page 5-----------------------
Long(System.currentTimeMillis()).toString().getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(id);
md.update(now);
return (toHex(md.digest()));
} catch (IllegalStateException e) {
return (null);
} catch (NoSuchAlgorithmException e) {
return (null);
}
}
总结
对于重复提交、重复刷新、防止后退等等都是属于系统为避免重复记录而需要解
决的问题,在客户端去处理需要针对每一种的可能提出相应的解决方案,然而在
服务器端看来只不过是对于数据真实性的检验问题,基于令牌的处理就是一劳永
逸的方法。
同时我们也看到,从不同的角度去看待问题,其解决的方法也是不同的。客户端
更追求的是用户的操作,而服务端则将注意力放在了数据的处理上,所以在某 个
对于服务器端看似容易的问题上,用客户端来解决却麻烦了很多!反之依然。所
以在某些问题的处理上我们需要综合考虑和平衡,是用客户端来解决?还是用服
务 器端来处理?
==============
jsp页面:
<%@ page contentType="text/html;charset=gbk"%>
<%@ page language="java" %>
<%@ page import="java.util.*"%>
<%
response.setHeader("Cache-Control", "no-cache");
String username="";//session.getAttribute("username")==null?"":(String)session.getAttribute("username");
%>
<%
Random rand = new Random();
Integer flag =new Integer(rand.nextInt());
String str_flag = flag.toString();
session.setAttribute("flag",str_flag);
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gbk" />
<title>test</title>
</head>
<body>
<table>
<form id="form1" action="usercheck.test" name="form1">
<tr>
<td width="25%" height="25" align="right">帐户:</td>
<td>
<input type=hidden name="flag" value="<%=str_flag%>"/>
<input name="username" type="text" class="text" id="account" value="<%=username %>" maxlength="10"/>
<span id="account_span"></span>
</td>
<td height="25" colspan="2">
<input id="jccf" style="margin-left:300px" type="button" name="Submit" onclick="checkusername();" value="提交"/>
</td>
</tr>
<tr>
<td>第四行赋的值是:</td>
<td><script>document.write(document.form1.flag.value)</script></td>
</tr>
</table>
</form>
<script script="javascript">
function checkusername()
{
form1.submit();
}
document.getElementById('username').focus();
</script>
</body>
</head>
Java:
package cn;
import java.io.*;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.*;
import cn.hello;
public class completeservlet extends HttpServlet{
hello h = new hello();
String session = "";
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession();
PrintWriter out= response.getWriter();
String returninfo ="";
String returnselectinfo = "";
String username = "";
username = request.getParameter("username").toString().trim();
Integer flag1 = new Integer(Integer.parseInt(request.getParameter("flag")));
String l_strflag1 = flag1.toString();
if(l_strflag1.equals(session.getAttribute("flag")))
{
returninfo =h.test(username);
returnselectinfo = h.SelectTest(username);
out.println("test is starting.....");
out.println("<P>");
out.println(returninfo);
out.println("<P>");
out.println(returnselectinfo);
out.println("<P>");
out.println("username:" + username);
session.removeAttribute("flag");
}
else
{
out.println("Don't submit repeatly.");
out.println(session.getAttribute("flag"));
out.println(flag1.toString());
session.removeAttribute("flag");
}
}
}
===========================
关于防止用户重复提交,要求在服务器端控制。
偶有个问题,大家讨论下。关于防止用户重复提交,要求能在不修改jsp的前提下实现(jsp页面很多,想在服务器端控制,现在网上的解决方法大都是在 jsp上做)。我现在是在session里做了个标志,能够解决他重复提交时我给他报个错误返回。但我想实现的是用户第一次提交查询,正常执行数据库操作,当用户第二次点击同样功能的时候,屏蔽掉他,不执行数据库操作,然后把第一次的结果返回给他。现在能屏蔽,但怎么把第一次的正确数据(第一次生成的页面)返回是个难题。我的系统环境jspweblogicoracle答:你把查询条件放到Session中就可以了答:每次传过来的查询条件跟Session中的比较下,相同保留,不相同更改,然后更新Session不就可以了吗?答:首先明确,多次提交应该是连续的动作就是提交成功1次后再做了1次首先可以客户端**,按钮按完后变灰然后session保存form内容,做比较,如果重复就返回答:可以在数据库中比较提交的数据啊。。。答: 不建议在session中保存数据,当数据量,访问量很大的时候,服务器就等着瘫痪吧;建议通过令牌(如Struts中StringToken)的机制;但是会涉及到Jsp,可以设计个公用性质的tag,替换Jsp中的默认Submit.关于"hellwindy(夜神·月)"提到的"首先可以客户端**,按钮按完后变灰然后session保存form内容,做比较,如果重复就返回".客户端**是个通用方法,但session中保存form内容,如果页面需要及时响应,那就不行了哦;:)个人看法,不足之处,请指正.GoodLuck!答:Struts 有个令牌可以用用~自己实现我有个小办法,写一个key.jsp:<@pagecontentType="text/html; charset=GBK"import="com.**.**.*">< Stringid=request.getSession().getId();id=MD5Code.MD5Encode(id); request.getSession().setAttribute("key",id);><inputtype="hidden"value="& lt;=id>"name="hh">这个页面加密个东西,我懒,用的SessionId,你用随即数什么的都行然后在表单里加< @includepage="key.jsp">就会生成一个隐藏<input>在处理数据时对比,处理完了把key清空-答:这样你的jsp也没什么大的改动,就是写个include,答:刚才在开会,先谢谢大家提供的方法。因为我们的这个是已经上线运行的系统,不能改动太大。struts的方法是不错,但对我不适用,jsp的方法,我开始就说了,也不适用。希望大家看看还有什么方法没,我也是很无奈啊,维护感觉比开发都麻烦。
============
JSP避免Form重复提交的三种方案
1 javascript ,设置一个变量,只允许提交一次。
<script language="javascript">
var checkSubmitFlg = false;
function checkSubmit() {
if (checkSubmitFlg == true) {
return false;
}
checkSubmitFlg = true;
return true;
}
document.ondblclick = function docondblclick() {
window.event.returnValue = false;
}
document.onclick = function doconclick() {
if (checkSubmitFlg) {
window.event.returnValue = false;
}
}
</script>
<html:form action="myAction.do" method="post" onsubmit="return checkSubmit();">
2 还是javascript,将提交按钮或者image置为disable
<html:form action="myAction.do" method="post"
onsubmit="getElById('submitInput').disabled = true; return true;">
<html:image styleId="submitInput" src="http://datadig.blog.163.com/blog/images/ok_b.gif" border="0" />
</html:form>
3 利用struts的同步令牌机制
利用同步令牌(Token)机制来解决Web应用中重复提交的问题,Struts也给出了一个参考实现。
基本原理:
服务器端在处理到达的请求之前,会将请求中包含的令牌值与保存在当前用户会话中的令牌值进行比较,看是否匹配。在处理完该请求后,且在答复发送给客户端之前,将会产生一个新的令牌,该令牌除传给客户端以外,也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次提交的话,客户端传过来的令牌就和服务器端的令牌不一致,从而有效地防止了重复提交的发生。
if (isTokenValid(request, true)) {
// your code here
return mapping.findForward("success");
} else {
saveToken(request);
return mapping.findForward("submitagain");
}
Struts根据用户会话ID和当前系统时间来生成一个唯一(对于每个会话)令牌的,具体实现可以参考TokenProcessor类中的generateToken()方法。
1. //验证事务控制令牌,<html:form >会自动根据session中标识生成一个隐含input代表令牌,防止两次提交
2. 在action中:
//<input type="hidden" name="org.apache.struts.taglib.html.TOKEN"
// value="6aa35341f25184fd996c4c918255c3ae">
if (!isTokenValid(request))
errors.add(ActionErrors.GLOBAL_ERROR,
new ActionError("error.transaction.token"));
resetToken(request); //删除session中的令牌
3. action有这样的一个方法生成令牌
protected String generateToken(HttpServletRequest request) {
HttpSession session = request.getSession();
try {
byte id[] = session.getId().getBytes();
byte now[] =
new Long(System.currentTimeMillis()).toString().getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(id);
md.update(now);
return (toHex(md.digest()));
} catch (IllegalStateException e) {
return (null);
} catch (NoSuchAlgorithmException e) {
return (null);
}
}
分享到:
相关推荐
JSP_重复提交_解决方法JSP_重复提交_解决方法JSP_重复提交_解决方法
NULL 博文链接:https://feng5588feng.iteye.com/blog/1494002
重复提交的分类: 1.由于网速原因而重复点击提交按钮 2.已经提交成功,然后又刷新页面重复提交 3.已经提交成功,然后点击后退,然后又重复提交。 重复提交的缺点: 1.加重了服务器的负担。 2.导致错误的操作。 实例...
使用sessionID和时间戳作为标识,关键代码如下: 代码如下:public class SswpdjAction extends BaseAction{ public String execute(){ /**业务代码**/ ……………. //设置标识 this.setSessionToken();...
jsp防止重复提交 这做最好结合让jsp页面过期一起用。当用户返回到上一表单页面时(按backspase键、返回按钮,右击--返回),让表单页面过期。这样才可以让用户主动的刷新jsp而从新生成一个session.setAttribute(...
本篇文章主要介绍了网页如何防止刷新重复提交与如何防止后退的解决方法,具体如下: 提交后禁用提交按钮(大部分人都是这样做的) 如果客户提交后,按F5刷新怎么办? 使用Session 在提交的页面也就是数据库处理之前: ...
第一种情况:提交完表单以后,不做其他操作,直接刷新页面,表单会提交多次。 – 在servlet中写一句输出,用来判断是否提交多次 System.out.println(已经插入);...– 解决方法:不用转发到另一页面,采用
在JSP页面中查询数据是会出现多次提交的事件,如何避免呢,这里个人见解,
6.6.4 sendredirect()和forward()方法的区别 238 6.7 小结 239 第7章 web应用程序的部署 240 7.1 配置任意目录下的web应用程序 240 7.2 war文件 242 7.3 tomcat中servlet的另一种运行方式 244 7.4 与servlet...
使用源生态Servlet+JSP+sqlserver实现登录、注册、文章增删改查功能,并解决增加文章重复提交问题,通过此小程序可以学习到普通动态网站前后台之间的关系,适合初学者学习。数据库也可以是Mysql,需要更换驱动程序。
6.6.4 sendredirect()和forward()方法的区别 238 6.7 小结 239 第7章 web应用程序的部署 240 7.1 配置任意目录下的web应用程序 240 7.2 war文件 242 7.3 tomcat中servlet的另一种运行方式 244 7.4 与servlet...
6.6.4 sendredirect()和forward()方法的区别 238 6.7 小结 239 第7章 web应用程序的部署 240 7.1 配置任意目录下的web应用程序 240 7.2 war文件 242 7.3 tomcat中servlet的另一种运行方式 244 7.4 与servlet...
6.6.4 sendredirect()和forward()方法的区别 238 6.7 小结 239 第7章 web应用程序的部署 240 7.1 配置任意目录下的web应用程序 240 7.2 war文件 242 7.3 tomcat中servlet的另一种运行方式 244 7.4 与servlet...
07 Struts标签 -LOGIC标签 08 Struts标签- HTML标签 09 Struts高级部分(1)(解决重复提交、上传组件) 10 Struts高级部分(2)(常用Action、验证框架、动态Form) 11 留言管理程序_使用Struts + DAO...
26、大数据量下的分页解决方法。 27、用 JDBC 查询学生成绩单, 把主要代码写出来(考试概率极大). 28、这段代码有什么不足之处? 29、说出数据连接池的工作机制是什么? 30、为什么要用 ORM? 和 JDBC 有何不一样?...
里面有很多常用的搭配的经典的例子,1-JSP+JDBC_假分页笔记 9-Struts高级部分(1)(解决重复提交、上传组件)笔记
1-JSP+JDBC_假分页笔记.pdf 2-JSP+JDBC_真分页(基于Oracle数据库分页)笔记.pdf 3-JSP+DAO和MVC+DAO(基于...9-Struts高级部分(1)(解决重复提交、上传组件)笔记.pdf 54留言管理程序_Struts + Spring + Hibernate笔记.pdf
9-Struts高级部分(1)(解决重复提交、上传组件)笔记 10-Struts高级部分(2)(常用Action、验证框架、动态Form)笔记 J2EE框架_笔记_b: 11-留言管理程序_使用Struts + DAO完成笔记 12-Struts + DAO分页笔记 16-...
9-Struts高级部分(1)(解决重复提交、上传组件)笔记 10-Struts高级部分(2)(常用Action、验证框架、动态Form)笔记 J2EE框架_笔记_b: 11-留言管理程序_使用Struts + DAO完成笔记 12-Struts + DAO分页笔记 16-...