(一)
两种防止重复调提交的方法
1 jsp页面提交
2 struts的action类型防止提交
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了
第二类, Struts的action类型的防止提交:
1,struts2的防止重复提交,也使用到了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/>标签,需要在页面中加上。
2,struts2的防止重复提交,也使用到了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>
注意:
a:includeMethods 指定需要拦截的方法 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);
}
}
分享到:
相关推荐
在Web开发中,表单重复提交是一个常见的问题,可能导致数据的冗余或者错误。PHP作为常用的服务器端脚本语言,提供了多种防止表单重复提交的方法。本文将详细探讨通过记录IP地址来防止表单重复提交的策略,并与其他...
问题的根源在于WebKit内核(包括Chrome和Safari)的浏览器为了防止用户意外或恶意地多次提交同一表单,采取了一种称为“防重复提交”的机制。这种机制会记住已经提交过的表单数据,如果用户尝试再次提交相同的表单,...
4. **表单操作**:支持表单的提交、重置、清空等常见操作,且能与后端接口无缝对接。 5. **灵活性**:允许添加自定义组件,扩展性强,满足不同业务场景的需求。 6. **响应式布局**:自动适应不同屏幕尺寸,提供...
- 清空原始的大数据字段,以避免重复提交。 - 最后,触发表单提交。 2. **服务器端处理**: - 在服务器端,使用`Request.Form()`获取所有隐藏字段的值,包括`hdnCount`字段。 - 根据`hdnCount`字段的值,循环...
- **表单值处理**:在提交前自动处理表单值,如去除空格、格式化日期等。 - **错误提示**:提供统一的错误提示展示,便于用户理解和修正错误。 - **状态管理**:管理表单的提交、重置、加载等状态,提供对应的钩子...
使用`ModelForm`,我们可以快速地创建基于模型的表单,这样表单字段会自动映射到模型字段,减少了重复代码。同时,通过模型,我们可以轻松地进行数据库操作,实现数据持久化。 总结,Django的表单和模型为开发者...
- 在表单提交之后,可以通过禁用提交按钮的方式来避免用户重复提交表单。 - 示例代码: ```javascript function onSub() { // 表单验证代码 document.getElementById('bsubmit').disabled = true; } ``` 2....
在JSF中,一个常见的问题是在用户点击刷新按钮时可能会导致重复提交,这可能导致数据的不一致性和其他问题。下面我们将详细讨论如何在JSF中防止页面刷新时的重复提交。 **一、问题解析** 当用户点击浏览器的刷新...
为避免大量重复代码,可以利用组件化思想,创建一个可复用的表单元素组件,如`InputComponent`,并通过`props`传递`value`和`v-model`属性。 ```html <!-- InputComponent.vue --> $emit('input', $event.target....
在IT领域,自动填表和提交是Web自动化测试和数据录入的重要组成部分,尤其在处理大量重复性输入时,能够显著提高工作效率。`webbrowser`模块是Python标准库的一部分,它提供了一个接口,允许我们控制默认的系统...
当然,实际应用中可能还需要考虑更多因素,如跨站请求伪造(CSRF)防护、重复提交的处理等。在开发过程中,始终要关注用户体验和安全性,合理地设计验证规则,避免过于严格导致用户体验下降,同时也要防止因验证不足...
只需定义JSON对象,表单组件就可以自动生成,减少了重复劳动。 2. **数据、逻辑和视图的分离**:在JSON表单中,数据模型(data)、业务逻辑(logic)和用户界面(view)是分开的。这种分离使得表单更易于理解和维护...
在网页开发中,表单提交验证是至关...以上就是关于JS表单提交验证、去除`input[type="number"]`的三角箭头以及刷新验证码的方法的详细说明。这些技巧可以帮助开发者更好地控制用户输入,提高用户体验,并确保数据安全。
总结来说,Spring Form标签库是Spring MVC框架中的一个重要组成部分,它为Java Web开发者提供了丰富的表单处理工具,减少了重复的手动编码,提高了代码的可维护性和可读性。通过理解并熟练运用Spring Form标签库,...
- **防止多次提交**:在全局 `onSubmit` 钩子中,可以通过设置 `request.vm.disabled = true` 来禁用表单,待 AJAX 请求完成后恢复,以防止重复提交。 Vue-Hook-Form 作为 Vue.js 1.x 的辅助工具,它使得表单处理...
在本教程中,我们将探讨如何利用AJAX(Asynchronous JavaScript and XML)技术来检测用户名是否重复,以提升用户体验,避免用户在提交表单后才收到用户名已存在的错误提示。 首先,让我们理解什么是AJAX。AJAX是一...
这样一来,浏览器不会保存提交后的表单数据,如果用户刷新页面,浏览器会因为找不到缓存而提示“网页已过期”,从而避免了重复提交。 以下是一个简单的示例,展示了如何在页面加载时禁用缓存并处理表单提交: ```...
PreparedStatement pstmt = conn.prepare(connection, "SELECT * FROM user WHERE name = ?"); pstmt.setString(1, username); ResultSet resultSet = pstmt.executeQuery(); if (resultSet.next()) { resp....
#### 五、处理表单提交 当用户填写完表单并点击“登录”按钮后,表单数据会被发送到服务器端。为了处理这些数据,我们需要创建一个处理POST请求的方法: ```python from flask import request @app.route("/login...
第一种方法通过修改内部属性来直接控制状态,操作较为简单,但可能需要额外的步骤来清除校验规则以避免提交时的重复校验。第二种方法则通过控制组件的渲染来间接解决问题,避免了对内部属性的直接修改,使DOM状态和...