现象:当页面进行过数据提交(PostBack)后,如果通过点击【F5】或者浏览器的【刷新】按钮,
此时页面会模拟浏览器的上次的提交事件,如果做的是数据插入操作,将有可能导致数据的多次插入。
原因:浏览器会模拟上次提交的数据在次提交,并在此触发上次提交的时间。
对策:
针对此,网上已经有各种各样的处理方法。大概有以下的两类:
1。通过在事件中,在插入数据之前做主键冲突判定。
2。在HttpHandler中添加票据处理,然后在事件处理中判断是否是刷新进行提交的。
两种方法都需要对事件做特殊的处理,对于项目维护的复杂度有所增加,尤其是第一种需要做数据的逻辑判断,已经影响到了业务逻辑,这是很不好的方法。
然而,我们是否可以让浏览器的刷新成为我们真正想要的刷新,即从新访问页面,而不是模拟上次的提交数据。
其实如果大家对第二种方式熟悉的话,他就是我们想要的方式,然而他需要每个页面的事件都做判断,因而维护程度较复杂,针对此本人做了一些改进。
具体修改方案如下:
1.在页面中声明两个HiddenField,第一个存储前台页面的票据,第二存储后台Session票据的Key。
2.如果前台的票据和Session的票据不相同,则即为刷新,此时DeterminePostBackMode方法返回空,以使页面置为 非PostBack的状态。
3.在OnPreRenderComplete中同步HiddenField和Session中的票据。
为了让各个页面对此事不可见的,将上述逻辑完全封装到Basepage中,使页面完全不需做其他处理。
实例:
BasePage:
BasePage
1
using
System;
2
using
System.Collections.Generic;
3
using
System.Linq;
4
using
System.Text;
5
6
namespace
DonkeyControl
7
{
8
public
class
BasePage : System.Web.UI.Page
9
{
10
private
static
string
PAGE_REFRESH_TICKET_KEY
=
"
__PageRefreshTicketKey
"
;
11
private
string
PAGE_SESSION_REFRESH_TICKET_KEY
=
"
__PageSessionRefreshTicketKey
"
;
12
13
///
<summary>
14
///
判断是否是PostBack
15
///
</summary>
16
///
<returns></returns>
17
protected
override
System.Collections.Specialized.NameValueCollection DeterminePostBackMode()
18
{
19
//
如果是通过"刷新"提交的后台,将页面强行设置成非PostBack模式,以实现刷新以重新的访问页面的形式进行
20
if
(IsRefreshed)
21
{
22
return
null
;
23
}
24
return
base
.DeterminePostBackMode();
25
}
26
27
protected
override
void
OnPreRenderComplete(EventArgs e)
28
{
29
SynchroRefreshTicket();
30
base
.OnPreRenderComplete(e);
31
}
32
33
///
<summary>
34
///
同步页面和后台Session的票据
35
///
</summary>
36
private
void
SynchroRefreshTicket()
37
{
38
if
(
!
Page.IsPostBack)
39
{
40
string
strTicket
=
Guid.NewGuid().ToString();
41
ClientScript.RegisterHiddenField(PAGE_REFRESH_TICKET_KEY, strTicket);
42
ClientScript.RegisterHiddenField(PAGE_SESSION_REFRESH_TICKET_KEY, strTicket);
43
Session[strTicket]
=
strTicket;
44
}
45
else
46
{
47
string
strTicket
=
Guid.NewGuid().ToString();
48
ClientScript.RegisterHiddenField(PAGE_REFRESH_TICKET_KEY, strTicket);
49
ClientScript.RegisterHiddenField(PAGE_SESSION_REFRESH_TICKET_KEY, Request.Form[PAGE_SESSION_REFRESH_TICKET_KEY]);
50
Session[Request.Form[PAGE_SESSION_REFRESH_TICKET_KEY]]
=
strTicket;
51
}
52
}
53
54
55
///
<summary>
56
///
比较HiddenField的票据和Session中存储的票据是否相同,如果不相同则为通过刷新的方式进行的提交
57
///
</summary>
58
protected
bool
IsRefreshed
59
{
60
get
61
{
62
if
(
string
.Equals(Request.Form.Get(PAGE_REFRESH_TICKET_KEY),Session[Request.Form.Get(PAGE_SESSION_REFRESH_TICKET_KEY)]))
63
{
64
return
false
;
65
}
66
else
67
{
68
return
true
;
69
}
70
}
71
}
72
}
73
}
74
测试页面:
Default.aspx
1
<%
@ Page Language
=
"
C#
"
AutoEventWireup
=
"
true
"
CodeBehind
=
"
Default.aspx.cs
"
Inherits
=
"
WebTest._Default
"
%>
2
3
<!
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>
4
<
html
xmlns
="http://www.w3.org/1999/xhtml"
>
5
<
head
runat
="server"
>
6
<
title
>
Untitled Page
</
title
>
7
</
head
>
8
<
body
>
9
<
form
id
="form1"
runat
="server"
>
10
<
div
>
11
<
asp:Label
ID
="Label1"
runat
="server"
></
asp:Label
>
12
<
asp:Button
ID
="Button1"
runat
="server"
Text
="Button"
/>
13
</
div
>
14
</
form
>
15
</
body
>
16
</
html
>
17
Default.aspx.cs 1 using System;
2 using System.Collections;
3 using System.Configuration;
4 using System.Data;
5 using System.Linq;
6 using System.Web;
7 using System.Web.Security;
8 using System.Web.UI;
9 using System.Web.UI.HtmlControls;
10 using System.Web.UI.WebControls;
11 using System.Web.UI.WebControls.WebParts;
12 using System.Xml.Linq;
13 using DonkeyControl;
14
15
16 namespace WebTest
17 {
18 public partial class _Default : BasePage
19 {
20 protected void Page_Load(object sender, EventArgs e)
21 {
22 if (this.IsRefreshed)
23 {
24 Label1.Text = "刷新";
25 }
26 else
27 {
28 Label1.Text = "正常postback";
29 }
30 }
31 }
32 }
33