`

关于from表单重复提交

阅读更多

(一)

两种防止重复调提交的方法

1  jsp页面提交

2  strutsaction类型防止提交

 

Jsp的页面提交类型:

1.将jsp页面中设计状态位,这样的话,改变状态位就不能进行支持操作。

<script language="javascript">   
    var checkSubmitFlg = false;   
    function checkSubmit() {     
        if (!checkSubmitFlg) { 
        checkSubmitFlg = true;     
        return true;  
        }else{
         alert("
不能重复提交
");
         return false;
        }
     }
</script>

<form action="XXXX" method="POST"   onsubmit="return     checkSubmit()">
 
内容
:<input type="text" name="content" value=""/><br>
  <input type="submit" value="
提交
"><br>
</form>

 

此类做法类似的还有:提交时将submit标签设置为不可操作的属性,或者当点了提交按钮后 使按钮不可用 或者可以在页面加个验证码 如果验证不通过就不跳转么!

 

下面是一种js解决方法:

通过js控制,提交后将提交按钮所在的table 通过
document.forms[0].submitType.value = v;
var v = document.getElementById(t);
v.innerHTML = "
数据提交中
...";

这种方式替换掉就ok

 

 

 

第二类, Strutsaction类型的防止提交:

       1struts2的防止重复提交,也使用到了token(令牌机制),并且使用到了struts2的一个叫token的拦截器

 

添加商品页面:

 

 

Jsp代码  

1.   <body>   

2.       <s:form action="product_doAddProduct.action" method="post">   

3.           <s:token></s:token>   

4.           <s:textfield name="name" label="商品名称"></s:textfield>   

5.           <s:textfield name="price" label="商品价格"></s:textfield>   

6.           <s:submit value="提交"></s:submit>   

7.       </s:form>   

8.   </body>  

<body>

  <s:form action="product_doAddProduct.action" method="post">

    <s:token></s:token>

    <s:textfield name="name" label="商品名称"></s:textfield>

    <s:textfield name="price" label="商品价格"></s:textfield>

    <s:submit value="提交"></s:submit>

  </s:form>

</body>

 

struts.xml 配置文件

Xml代码  

1.   <package name="token" extends="struts-default" namespace="/jsp">  

2.     <action name="product_*" class="org.cric.action.ProductAction" method="{1}">

3.         <result name="success">/jsp/product.jsp</result>  

4.        <result name="invalid.token">/jsp/error.jsp?message=重复提交</result>  

5.          

1.     <interceptor-ref name="token"/>  

1.   <SPAN style="WHITE-SPACE: pre"> </SPAN><interceptor-ref name="defaultStack"/>  

6.       </action>  

7.    </package>  

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

     <action name="product_*" class="org.cric.action.ProductAction" method="{1}">

       <result name="success">/jsp/product.jsp</result>

       <result name="invalid.token">/jsp/error.jsp?message=重复提交</result>

       <interceptor-ref name="token"/>

       <interceptor-ref name="defaultStack"/>

     </action>

 </package>

 

动作层:

Java代码  

1.   public String doAddProduct() throws Exception{   

2.           System.out.println("添加商品了……");   

3.           Thread.sleep(10000);   

4.           return "success";   

5.       }  

public String doAddProduct() throws Exception{

    System.out.println("添加商品了……");

    Thread.sleep(10000);

    return "success";

  }

 

错误页面:

Html代码  

1.   <body>  

2.   ${param['message']}   

3.   </body>  

<body>

${param['message']}

</body>

 

显示商品信息页面:

Html代码  

1.   <body>  

2.       商品名称:<s:property value="%{name}"/><br>  

3.       商品价格:<s:property value="%{price}"/>  

4.   </body>  

<body>

  商品名称:<s:property value="%{name}"/><br>

  商品价格:<s:property value="%{price}"/>

</body>

 

注意:

 

a:当拦截器拦截到 当发生重复提交的action时候,会跳转到invalid.token指定的页面。

b:为了使用同一的错误页面,在错误页面上接受到message参数的值,可以在error.jsp使用 ${param['message']}

c:在提交页面的form中添加<s:token/>标签,需要在页面中加上。

 

       2struts2的防止重复提交,也使用到了token(令牌机制),并且使用到了struts2的一个叫token-session的拦截器

    Token Session拦截器扩展了Token并提供了一种更复杂的服务。Token Session拦截器不想Token拦截器那样会返回一种特殊的结果并添加一个动作错误,它采用的做法只是阻断后续的提交,而这么做的后果是用户将看到同一的响应,就好像只有一次提交那样!

 

Xml代码  

1.   <package name="token" extends="struts-default" namespace="/jsp">  

2.     <action name="product_*" class="org.cric.action.ProductAction" method="{1}">

3.       <result name="success">/jsp/product.jsp</result>  

4.               <interceptor-ref name="token-session">  

5.                  <param name="includeMethods">doAddProduct</param>  

6.               </interceptor-ref>  

7.                   <interceptor-ref name="defaultStack"/>  

8.               </action>  

9.     </package> <package name="token" extends="struts-default" namespace="/jsp">

       <action name="product_*" class="org.cric.action.ProductAction" method="{1}">

         <result name="success">/jsp/product.jsp</result>

         <interceptor-ref name="token-session">

           <param name="includeMethods">doAddProduct</param>

         </interceptor-ref>

         <interceptor-ref name="defaultStack"/>

       </action>

     </package>

 

注意:

aincludeMethods 指定需要拦截的方法 excludeMethods 指定不需要拦截的方法,多个方法使用逗号分隔

b:把第一个程序的配置文件换成这个,不需要错误页面。

 

 

<s:token/>的原理就是在session总保存一个字符串。
仔细观察包含<s:token/>的页面会发现这些东西
<input type="hidden" name="struts.token.name" value="struts.token"/>
<input type="hidden" name="struts.token" value="BXPNNDG6BB11ZXHPI4E106CZ5K7VNMHR"/>
当页面提交的时候时候他会判断这个值是不是和session的一样,如果是一样的哪么OK,没问题你可以提交,并且会修改session里的值。哪么如果你刷新,那你页面上的值和session的值就不一样的,struts就不允许你提交数据了。就是这个原理。

 

 

 

我们是用了最笨的方法.是针对refresh页面时重复提交的问题.当进入一张表单时往session写个值设为true,提交后在action处理的时候往session里设这个值为false,在方法里判段它为true时才执行数据库等操作.就杜绝了重复提交.

 

 

 

forward设置redirect=true是最简单的设置了也是最容易想到的啊

 

Struts已经内置了能够防止用户重复提交同一个HTML表单的功能。它采用的办法在其他一些用来开发Web应用程序的技术里也可以见到:让服务器生成一个唯一标记,并在服务器和表单里保存一份这个标记的副本。此后,在用户提交表单的时候,表单里的标记将虽说这其他请求参数一起发送到服务器,服务器将对它收到的标记和它留存的标记进行比较。如果两者匹配,这次从提交来的表单就是被认为是有效的,服务器将对之做出必要的处理并重新设置一个标记。随后(因为不小心)提交相同的表单就会失败,因为服务器上的标记已经重置。

 

<s:token/>的原理就是在session总保存一个字符串。
仔细观察包含<s:token/>的页面会发现这些东西
<input type="hidden" name="struts.token.name" value="struts.token"/>
<input type="hidden" name="struts.token" value="BXPNNDG6BB11ZXHPI4E106CZ5K7VNMHR"/>
当页面提交的时候时候他会判断这个值是不是和session的一样,如果是一样的哪么OK,没问题你可以提交,并且会修改session里的值。哪么如果你刷新,那你页面上的值和session的值就不一样的,struts就不允许你提交数据了。就是这个原理。

 

 

 

我们是用了最笨的方法.是针对refresh页面时重复提交的问题.当进入一张表单时往session写个值设为true,提交后在action处理的时候往session里设这个值为false,在方法里判段它为true时才执行数据库等操作.就杜绝了重复提交.

 

 

 

forward设置redirect=true是最简单的设置了也是最容易想到的啊

 

Struts已经内置了能够防止用户重复提交同一个HTML表单的功能。它采用的办法在其他一些用来开发Web应用程序的技术里也可以见到:让服务器生成一个唯一标记,并在服务器和表单里保存一份这个标记的副本。此后,在用户提交表单的时候,表单里的标记将虽说这其他请求参数一起发送到服务器,服务器将对它收到的标记和它留存的标记进行比较。如果两者匹配,这次从提交来的表单就是被认为是有效的,服务器将对之做出必要的处理并重新设置一个标记。随后(因为不小心)提交相同的表单就会失败,因为服务器上的标记已经重置。

 

 (二)

Struts的Token(令牌)机制能够很好的解决表单重复提交的问题,基本原理是:服务器端在处理到达的请求之前,会将请求中包含的令牌值与保存在当前用户会话中的令牌值进行比较,看是否匹配。在处理完该请求后,且在答复发送给客户端之前,将会产生一个新的令牌,该令牌除传给客户端以外,也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次提交的话,客户端传过来的令牌就和服务器端的令牌不一致,从而有效地防止了重复提交的发生。

 

这时其实也就是两点,第一:你需要在请求中有这个令牌值,请求中的令牌值如何保存,其实就和我们平时在页面中保存一些信息是一样的,通过隐藏字段来保存,保存的形式如: 〈input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae"〉,这个value是TokenProcessor类中的generateToken()获得的,是根据当前用户的session id和当前时间的long值来计算的。第二:在客户端提交后,我们要根据判断在请求中包含的值是否和服务器的令牌一致,因为服务器每次提交都会生成新的Token,所以,如果是重复提交,客户端的Token值和服务器端的Token值就会不一致。下面就以在数据库中插入一条数据来说明如何防止重复提交。

在Action中的add方法中,我们需要将Token值明确的要求保存在页面中,只需增加一条语句:saveToken(request);,如下所示:
public ActionForward add(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
//前面的处理省略
saveToken(request);
return mapping.findForward("add");
}
在Action的insert方法中,我们根据表单中的Token值与服务器端的Token值比较,如下所示:
public ActionForward insert(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
if (isTokenValid(request, true)) {
// 表单不是重复提交
//这里是保存数据的代码
} else {
//表单重复提交
saveToken(request);
//其它的处理代码
}
}

其实使用起来很简单,举个最简单、最需要使用这个的例子:
一般控制重复提交主要是用在对数据库操作的控制上,比如插入、更新、删除等,由于更新、删除一般都是通过id来操作(例如:updateXXXById, removeXXXById),所以这类操作控制的意义不是很大(不排除个别现象),重复提交的控制也就主要是在插入时的控制了。

先说一下,我们目前所做项目的情况:
目前的项目是用Struts+Spring+Ibatis,页面用jstl,Struts复杂View层,Spring在Service层提供事务控制,Ibatis是用来代替JDBC,所有页面的访问都不是直接访问jsp,而是访问Structs的Action,再由Action来Forward到一个Jsp,所有针对数据库的操作,比如取数据或修改数据,都是在Action里面完成,所有的Action一般都继承BaseDispatchAction,这个是自己建立的类,目的是为所有的Action做一些统一的控制,在Struts层,对于一个功能,我们一般分为两个Action,一个Action里的功能是不需要调用Struts的验证功能的(常见的方法名称有add,edit,remove,view,list),另一个是需要调用Struts的验证功能的(常见的方法名称有insert,update)。

就拿论坛发贴来说吧,论坛发贴首先需要跳转到一个页面,你可以填写帖子的主题和内容,填写完后,单击“提交”,贴子就发表了,所以这里经过两个步骤:
1、转到一个新增的页面,在Action里我们一般称为add,例如:
public ActionForward add(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        //这一句是输出调试信息,表示代码执行到这一段了
        log.debug(":: action - subject add");    
    
        //your code here

       //这里保存Token值
        saveToken(request);
        
       //跳转到add页面,在Structs-config.xml里面定义,例如,跳转到subjectAdd.jsp
        return mapping.findForward("add");
    }

2、在填写标题和内容后,选择 提交 ,会提交到insert方法,在insert方法里判断,是否重复提交了。
public ActionForward insert(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response){
if (isTokenValid(request, true)) {
// 表单不是重复提交
//这里是保存数据的代码
} else {
//表单重复提交
saveToken(request);
//其它的处理代码
}
}

下面更详细一点(注意,下面所有的代码使用全角括号):
1、你想发贴时,点击“我要发贴”链接的代码可以里这样的:
〈html:link action="subject.do?method=add"〉我要发贴〈/html:link〉
subject.do 和 method 这些在struct-config.xml如何定义我就不说了,点击链接后,会执行subject.do的add方法,代码如上面说的,跳转到subjectAdd.jsp页面。页面的代码大概如下:
〈html:form action="subjectForm.do?method=insert"〉
  〈html:text property="title" /〉
  〈html:textarea property="content" /〉
  〈html:submit property="发表" /〉
  〈html:reset property="重填" /〉
〈html:form〉
如果你在add方法里加了“saveToken(request);”这一句,那在subjectAdd.jsp生成的页面上,会多一个隐藏字段,类似于这样〈input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae"〉,

2、点击发表后,表单提交到subjectForm.do里的insert方法后,你在insert方法里要将表单的数据插入到数据库中,如果没有进行重复提交的控制,那么每点击一次浏览器的刷新按钮,都会在数据库中插入一条相同的记录,增加下面的代码,你就可以控制用户的重复提交了。
if (isTokenValid(request, true)) {
// 表单不是重复提交
//这里是保存数据的代码
} else {
//表单重复提交
saveToken(request);
//其它的处理代码
}
注意,你必须在add方法里使用了saveToken(request),你才能在insert里判断,否则,你每次保存操作都是重复提交。
记住一点,Struts在你每次访问Action的时候,都会产生一个令牌,保存在你的Session里面,如果你在Action里的函数里面,使用了saveToken(request);,那么这个令牌也会保存在这个Action所Forward到的jsp所生成的静态页面里。
如果你在你Action的方法里使用了isTokenValid,那么Struts会将你从你的request里面去获取这个令牌值,然后和Session里的令牌值做比较,如果两者相等,就不是重复提交,如果不相等,就是重复提交了。

由于我们项目的所有Action都是继承自BaseDispatchAction这个类,所以我们基本上都是在这个类里面做了表单重复提交的控制,默认是控制add方法和insert方法,如果需要控制其它的方法,就自己手动写上面这些代码,否则是不需要手写的,控制的代码如下:
public abstract class BaseDispatchAction extends BaseAction {
protected ActionForward perform(ActionMapping mapping, ActionForm form,
    HttpServletRequest request, HttpServletResponse response)
    throws Exception {
        String parameter = mapping.getParameter();
        String name = request.getParameter(parameter);
if (null == name) { //如果没有指定 method ,则默认为 list
            name = "list";
        }

        if ("add".equals(name)) {
            if ("add".equals(name)) {
                saveToken(request);
            }
        } else if ("insert".equals(name)) {
            if (!isTokenValid(request, true)) {
                resetToken(request);
                saveError(request, new ActionMessage("error.repeatSubmit"));
                log.error("重复提交!");
                return mapping.findForward("error");
            }
        }
        return dispatchMethod2(mapping, form, request, response, name);
    }
}

 

分享到:
评论

相关推荐

    JSON生成Form表单的方法示例

    每次开发一个表单页的时候,都需要重复编写表单组件及其交互事件的代码,这块代码重复编写且与主线业务逻辑无关,除此之外,表单的校验、缓存等额外功能,也需要不少的代码量,这样就造成了一个表单页的代码量庞大

    Elefant CMS 2.2.5

    Elefant CMS 2.2.5 更新日志:2020-11-20改进之处:添加了ELEFANT_SITE_NAME,ELEFANT_EMAIL_FROM和ELEFANT_DOMAIN环境变量;...Bug修复:固定的表单在指定skip_if_empty时不提交,但在表单的HTML中找不到该字段。

    禅道项目管理软件 6.2.stable 版

    1256 对表单提交过来的字段进行specialchars处理。 1022 添加需求,任务,bug,文档的时候判断是否重复。 1237 任务记录工时的时候在表zt_taskestimate中记录account属性的值 905 文档可以修改其所属的文档库 1167 ...

    图书管理系统 大二上学期的期末大作业。感觉是一个不错的Javaweb的练手项目hh。

    注册的时候不要以form表单的形式直接提交信息,要采用ajax,以json的形式,向后台提交信息。 学生登录页面: 登录信息:学号、密码登录 用cookie(加密)使十天内记住账号信息 (提示:为了您的信息安全,请不要在...

    lotus利用ODBC类操作SQL数据库代码--已通过

    Messagebox "系统已经存在,请不要重复录入",,"警告" continue=False Exit Sub Else j = 0 For i = 1 To rs.NumRows rs.CurrentRow = i If Cstr(rs.GetValue("Cname")) = Cstr(uidoc....

    ExtAspNet_v2.3.2_dll

    -为按钮增加DisableControlBeforePostBack属性 - 回发之前是否禁用按钮,防止重复提交 - 默认为true。 -Grid的Values属性访问限制由internal改为public,这就意味这可以自由改变Grid中每个单元格的值了。 -增加...

    ExtAspNet v2.2.1 (2009-4-1) 值得一看

    -为按钮增加DisableControlBeforePostBack属性 - 回发之前是否禁用按钮,防止重复提交 - 默认为true。 -Grid的Values属性访问限制由internal改为public,这就意味这可以自由改变Grid中每个单元格的值了。 -增加...

    Spring面试题

    用户提交表单时,一个配置好的ActionForm对象被创建,并被填入表单相应的数据,ActionServler根据Struts-config.xml文件配置好的设置决定是否需要表单验证,如果需要就调用ActionForm的 Validate()验证后选择将...

    UIWebView的使用代码

    UIWebViewNavigationTypeFormResubmitted,用户重复提交表单 UIWebViewNavigationTypeOther,发生其它行为。 因此,实现用户触击UIWebView页面中的链接,并跳至Safari中打开链接页面的步骤如下: 定义实现...

    CodeIgniter:php敏捷开发框架web快速开发详解

    }上述函数捕获像“&”这样的特殊字符,以便在你的页面提交时不会造成混乱。你应该知道,有些字符会引起问题。 并不是所有的用户都会中规中矩的输入符合要求的信息,你也不可能知道使用浏览器输入信息的是什么人,...

    Python核心编程第二版(ok)

    Python核心编程第二版(ok) 第1部分 Python核心  第1章 欢迎来到Python世界   1.1 什么是Python   1.2 起源   1.3 特点  ... 12.4.2 from-import语句   12.4.3 多行导入   12.4.4...

    Python核心编程第二版

    很不错的python书 第1部分 Python核心  第1章 欢迎来到Python世界   1.1 什么是Python   1.2 起源   1.3 特点   1.3.1 高级   1.3.2 面向对象  ... 12.4.2 from-import语句   12.4.3 ...

    asp.net知识库

    SubmitOncePage:解决刷新页面造成的数据重复提交问题 SharpRewriter:javascript + xml技术利用#实现url重定向 采用XHTML和CSS设计可重用可换肤的WEB站点 asp.net的网址重定向方法的比较:面向搜索引擎友好 也谈 ...

    mahara教师评价系统

    Moodle 注重全 面的安全性,所有的表单都被检查,数据都被校验, cookie 是被加密的。用户注册时,通 过电子邮件进行首次登陆, 且同一个邮件地址不能在同一门课程中进行重复注册, 所有这些, 都使得 Moodle 的...

    C#编程经验技巧宝典

    C#编程经验技巧宝典源代码,目录如下: 第1章 开发环境 1 &lt;br&gt;1.1 Visual Studio开发环境安装与配置 2 &lt;br&gt;0001 安装Visual Studio 2005开发环境须知 2 &lt;br&gt;0002 配置合适的Visual Studio 2005...

Global site tag (gtag.js) - Google Analytics