用过ASP.NET的都知道,按钮操作之后会得到一个新的页面,然后用户通过浏览器的回退按钮可以回到原来的页面时,或者点刷新按钮刷新当前页面时,会显示对话框"不重新发送信息,则无法刷新页面",然后就有个"重试","取消"按钮,通常用户为了刷新页面而点击"重试",这样一点问题就来了,那么页面就会重复发送信息,并且执行先前按钮的操作,比如按钮是添加一行数据,那么刷新之后就会添加重复的数据.好的程序或许会报个提示说什么不能重复添加,甚至出个异常,没有限制的程序可能就任由用户重复的添加数据.这样当然是开发人员不想发生的,除非有极端的用户需求.
最近学了下HttpModule的内容,可用来解决这个问题.
这里利用HttpModule在页面请求之前,做一些操作,用一些标记标识每个请求的页面,程序如下,相互学习下.
首先有个类继承IHttpModule
NoRepeatOperModule
1public class NoRepeatOperModule : IHttpModule
2 {
3 /**//// <summary>
4 /// 保存访问的页面记录,以页面路径为key, 标号为value
5 /// </summary>
6 static Hashtable historyRequest = null;
7
8 /**//// <summary>
9 /// 请求路径
10 /// </summary>
11 private string RequestPath
12 {
13 get
14 {
15 return HttpContext.Current.Request.Path;
16 }
17 }
18
19 IHttpModule 成员#region IHttpModule 成员
20
21 public void Dispose()
22 {
23 //throw new Exception("The method or operation is not implemented.");
24 }
25
26 public void Init(HttpApplication context)
27 {
28 if (historyRequest == null)
29 {
30 historyRequest = new Hashtable();
31 }
32
33 context.BeginRequest += delegate(object sender, EventArgs e)
34 {
35 int lastTicket = GetLastTicket();
36 int currentTicket = GetCurrentTicket(lastTicket);
37
38 // 比较当前标号和上一个的标号,判断页面请求是否来源于刷新操作
39 // 当前标号大于上一个标号 或初次请求都属于新的页面
40 if (currentTicket > lastTicket || (lastTicket == currentTicket && currentTicket == 0))
41 {
42 // 标记并保存页面请求是否来源于刷新的bool值
43 HttpContext.Current.Items[RequestPath + "_IsRefreshed"] = false;
44 historyRequest[RequestPath] = currentTicket;
45 }
46 else
47 {
48 HttpContext.Current.Items[RequestPath + "_IsRefreshed"] = true;
49 }
50 };
51 }
52
53 #endregion
54
55 /**//// <summary>
56 /// 获取某页面的上一个标号
57 /// </summary>
58 /// <returns></returns>
59 private int GetLastTicket()
60 {
61 if (!historyRequest.ContainsKey(RequestPath))
62 {
63 return 0;
64 }
65
66 return int.Parse(historyRequest[RequestPath].ToString());
67 }
68
69 /**//// <summary>
70 /// 获取页面的当前标号
71 /// </summary>
72 /// <param name="lastTicket">上一个标号</param>
73 /// <returns></returns>
74 private int GetCurrentTicket(int lastTicket)
75 {
76 int ticket;
77 // CurrentTicket 为页面的隐藏域的内容
78 string currentTicket = HttpContext.Current.Request["CurrentTicket"];
79 if (currentTicket == null)
80 {
81 ticket = lastTicket;
82 }
83 else
84 {
85 ticket = int.Parse(currentTicket);
86 }
87
88 // 保存页面的下一个标号
89 HttpContext.Current.Items[RequestPath + "_NextTicket"] = ticket + 1;
90 return ticket;
91 }
92 }
第二步,在web.config中配置自定义的HttpModule
Web.Config配置
1<system.web>
2 <httpModules>
3 <add name="NoRepeatOperModule" type="AspAdvance1.NoRepeatOperModule, AspAdvance1"/>
4 </httpModules>
5</system.web>
最后还要在asp.cs文件中处理如下,加个属性IsRefreshed,重写基类的
OnPreRenderComplete,最后调用在
btnTest_Click
其实可以吧以下作为自定义的Page类,其他页面继承即可
具体页面实现
1public partial class _Default : System.Web.UI.Page
2 {
3 /**//// <summary>
4 /// 页面请求是否来源于页面刷新
5 /// </summary>
6 private bool IsRefreshed
7 {
8 get
9 {
10 string requestPath = HttpContext.Current.Request.Path;
11 return (bool)HttpContext.Current.Items[requestPath + "_IsRefreshed"];
12 }
13 }
14
15 /**//// <summary>
16 /// 重写OnPreRenderComplete,生成隐藏域CurrentTicket
17 /// </summary>
18 /// <param name="e"></param>
19 protected override void OnPreRenderComplete(EventArgs e)
20 {
21 base.OnPreRenderComplete(e);
22 string requestPath = HttpContext.Current.Request.Path;
23 int ticket = int.Parse(HttpContext.Current.Items[requestPath + "_NextTicket"].ToString());
24 ClientScript.RegisterHiddenField("CurrentTicket", ticket.ToString());
25 }
26
27 protected void btnTest_Click(object sender, EventArgs e)
28 {
29 // 根据IsRefreshed 判断页面请求是否来源于刷新来 判断是否进行操作
30 if (!IsRefreshed)
31 {
32 using (StreamWriter sw = new StreamWriter("E:\\test.txt", true))
33 {
34 sw.WriteLine(DateTime.Now.ToString());
35 }
36 }
37 }
38 }
点击btnTest之后会写入一行,此时页面重新生成,点刷新或回退,再点"重试",将不会重复原来的操作.