電商項目中,用戶可能經常要下訂單。這時有一點需要我們考慮到:防止訂單重復提交。
也就是說,我下單成功后,刷新頁面,再次生成訂單就會產生誤會了。用戶明明下了一次單,你卻給人家發兩次貨,給人家收兩次錢。
那么我們分析一下造成訂單重復提交的原因有哪些?
一是轉發引起的。
我們知道頁面跳轉有兩種方式:轉發和重定向。所謂轉發是在服務器端進行跳轉,對用戶時透明的,這時,瀏覽器中的地址是不會改變的。所以,如果此時刷新頁面,會再次對服務器請求一次,造成多次下單;
而重定向,是在客戶端發生跳轉,跳轉時瀏覽器中的地址會發生改變。也就是說,我下單成功后,會馬上重定向到一個新頁面,瀏覽器地址欄改變,這是刷新,只會請求顯示的地址,而不會請求上一次的地址。
*注:重定向如果需要傳遞參數,可以采用下面方式
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;
}
}
|