現象:當頁面進行過數據提交(PostBack)後,如果通過點擊【F5】或者瀏覽器的【刷新】按鈕,
此時頁面會模擬瀏覽器的上次的提交事件,如果做的是數據插入操作,將有可能導致數據的多次插入。
原因:瀏覽器會模擬上次提交的數據在次提交,並在此觸發上次提交的時間。
對策:
針對此,網上已經有各種各樣的處理方法。大概有以下的兩類:
1。通過在事件中,在插入數據之前做主鍵衝突判定。
2。在HttpHandler中添加票據處理,然後在事件處理中判斷是否是刷新進行提交的。
兩種方法都需要對事件做特殊的處理,對於項目維護的複雜度有所增加,尤其是第一種需要做數據的邏輯判斷,已經影響到了業務邏輯,這是很不好的方法。
然而,我們是否可以讓瀏覽器的刷新成爲我們真正想要的刷新,即從新訪問頁面,而不是模擬上次的提交數據。
其實如果大家對第二種方式熟悉的話,他就是我們想要的方式,然而他需要每個頁面的事件都做判斷,因而維護程度較複雜,針對此本人做了一些改進。
具體修改方案如下:
1.在頁面中聲明兩個HiddenField,第一個存儲前臺頁面的票據,第二存儲後臺Session票據的Key。
2.如果前臺的票據和Session的票據不相同,則即爲刷新,此時DeterminePostBackMode方法返回空,以使頁面置爲 非PostBack的狀態。
3.在OnPreRenderComplete中同步HiddenField和Session中的票據。
爲了讓各個頁面對此事不可見的,將上述邏輯完全封裝到Basepage中,使頁面完全不需做其他處理。
實例:
BasePage:
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
測試頁面:
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
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