电商项目中,用户可能经常要下订单。这时有一点需要我们考虑到:防止订单重复提交。
也就是说,我下单成功后,刷新页面,再次生成订单就会产生误会了。用户明明下了一次单,你却给人家发两次货,给人家收两次钱。
那么我们分析一下造成订单重复提交的原因有哪些?
一是转发引起的。
我们知道页面跳转有两种方式:转发和重定向。所谓转发是在服务器端进行跳转,对用户时透明的,这时,浏览器中的地址是不会改变的。所以,如果此时刷新页面,会再次对服务器请求一次,造成多次下单;
而重定向,是在客户端发生跳转,跳转时浏览器中的地址会发生改变。也就是说,我下单成功后,会马上重定向到一个新页面,浏览器地址栏改变,这是刷新,只会请求显示的地址,而不会请求上一次的地址。
*注:重定向如果需要传递参数,可以采用下面方式
1
2
3
|
<result name=
"success"
type=
"redirect"
>/test/addSuccess.action?productId=${productId}</result>
</action>
|
二是服务器响应慢引起的。
当用户点击下订单后,由于服务器反应时间过长没能及时看到响应信息,或者出于其它目的,多次点击“提 交”按钮,从而导致在服务器端接收到两条或多条相同的信息。如果信息需要存储到后台数据库中,如此以来就会产生数据库操作异常提示信息,以至于给用户带来 错误信息提示,从而给用户的使用带来不便。
如何解决?
以下思路,可以当个参考:
1、下单成功后,重定向到其他页面
这种方式,只能解决刷新时,生成订单的可能,但是不能防止多次点击的情况。
2、利用struts2的token拦截器
原理:
Struts 2已经内置了能够防止用户重复提交同一个HTML表单的功能。它的工作原理:让服务器生成一个唯一标记,并在服务器和表单里各保存一份这个标记的副本。此 后,在用户提交表单的时候,表单里的标记将随着其他请求参数一起发送到服务器,服务器将对他收到的标记和它留存的标记进行比较。如果两者匹配,这次提交的 表单被认为是有效的,服务器将对之做出必要的处理并重新设置一个新标记。随后,提交相同的表单就会失败,因为服务器上的标记已经重置。
Struts 2标签中的token标签,可以用来生成一个独一无二的标记。这个标记必须嵌套在form标签中使用,它会在表单里插入一个隐藏字段并把标记保存到 HttpSession对象里。toke标签必须与Token或Token Session拦截器配合使用,两个拦截器都能对token标签进行处理。Token拦截器遇到重复提交表单的情况,会返回一 个"invalid.token"结果并加上一个动作级别的错误。Token Session拦截器扩展了Token拦截器并提供了一种更复杂的服务,它采取的做法与Token拦截器不同,它只是阻断了后续的提交,这样用户不提交多 少次,就好像只是提交了一次。
附Struts2的token拦截器demo一个:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
<%@ page language=
"java"
contentType=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+
"://"
+request.getServerName()+
":"
+request.getServerPort
()+path+
"/"
;
%>
<%
@taglib
uri=
"/struts-tags"
prefix=
"s"
%>
<meta charset=
"utf-8"
>
<s:form id=
"payFrm"
action=
""
>
<s:hidden type=
"hidden"
name=
"bankCardsId"
id=
"bankCardsId"
value=
"'0'/"
>
<s:hidden type=
"hidden"
name=
"productId"
value=
"%{#request.productId}"
>
<s:token></s:token>
</s:hidden></s:hidden></s:form>
<span
class
=
"sureBuy"
>确认订单</span>
<script type=
"text/javascript"
src=
"<%=basePath %>weixin/js/fx.js"
></script>
<script type=
"text/javascript"
src=
"<%=basePath %>weixin/js/fx_methods.js"
></script>
<script>
$(function(){
$(
".sureBuy"
).click(function(){
var form = document.getElementById(
"payFrm"
);
form.action =
"<%=path%>"
+/test/sureOrder.action;
form.method =
"post"
;
form.submit();
});
</script>
|
struts2配置文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<
package
name=
"mallOrderAction"
extends
=
"json-default"
namespace=
"/test"
>
<interceptor-ref name=
"defaultStack"
>
<interceptor-ref name=
"token"
>
<result name=
"invalid.token"
type=
"redirect"
>/test/payRepeat.action</result>
<result name=
"error"
>/test/payError.jsp</result>
<result name=
"success"
type=
"redirect"
>/test/orderSuccess.action?productId=${productId}</result>
</interceptor-ref></interceptor-ref></action>
<result name=
"success"
>/test/payRepeat.jsp</result>
</action>
<result name=
"success"
>/test/orderSuccess.jsp</result>
</action>
</
package
>
|
action类文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
public
class
orderAction
extends
ActionSupport{
/**
* @MethodName : sureOrder
* @Description : 添加订单
* @return
* @throws Exception
*/
public
String sureOrder()
throws
Exception{
//………下单的各种步骤…………………
return
SUCCESS;
}
/**
* @MethodName : payRepeat
* @Description : 重复提交
* @return
*/
public
String payRepeat(){
return
SUCCESS;
}
/**
* @MethodName : addSuccess
* @Description : 下单成功
* @return
*/
public
String addSuccess(){
HttpServletRequest request = ServletActionContext.getRequest();
Product product = product.getproductId(productId);
request.setAttribute(
"product"
, product);
return
SUCCESS;
}
}
|