ASP.NET 如何避免页面重新整理时重复送出

有些使用者的行为真是令人猜不透…,开网页有事没事就来给你 Refresh 一下,这个动作看似无害,但是在刚执行过 Submit 的情况下,Refresh 网页会造成重复执行,这也是为什么在各大购物网站的交易付款动作,都会提示「不要关闭网页或重新整理避免造成交易失败或重复交易」这一类的讯息,但根据经验,就算在网页上提出警告了,仍有为数不少的使用者依然会 Refresh 网页。

注意,别以为只有 ASP.NET 才有这种问题,这问题普遍存在于网页程式,不管你用何种平台、语言开发,这肇因于浏览器会自行 Cache 使用者的浏览行为 (包含资料),测试过 IE、FireFox、Chrome 都一样,猜想是因为这样才能有上一页、下一页的歷程纪录,至于更进一步的探讨,小的力有未逮就不再深究 (欢迎瞭解的前辈高人指点一下迷津,说说缘由)…既然浏览器塬始设计如此,而我们可能永远都猜不透使用者爱怎么操作,那就针对 ASP.NET 的开发来看看有甚么方式可以解决这样的问题。

不知道有没有人跟我一样,马上想到的是:重新导向,也就是在执行某一工作成功之后,执行 Response.Redirect 方法重导到结果页面,这是最典型的作法,不过这比较适用在动作连贯的多重网页表单,例如:购物车,在完成结帐后就可以导到订购成功的讯息页面,反正前面也从第一步、第二步…到结帐画面了,再多导一次已经差别不大。可惜多重网页表单毕竟是少数,大部分网页程式现在几乎都要求非同步更新 (AJAX),最好在同一画面完成所有动作,即便今天不要求非同步更新,每个作业完成之后都导到另一个网页,也不甚理想,所以这种作法并不完美,除了多维护一个网页的麻烦不说,事实上使用者若先回到上一页再重新整理,一样可能会造成重复执行…。

那在任何异动前,先检查是否有相同资料存在呢?换句话说是在资料库端检查,应该可行,不过…过程似乎稍嫌繁琐,要针对每一个作业内容个别去撰写比对是否有相同资料的逻辑,光想就觉得累了…,况且有时候确实是可以允许相同资料存在,比如说线上客服的留言版,使用者不耐久候时,会再留言一次,内容可能跟前次一模一样,这跟重刷页面造成的资料重复是不可相提并论的,这样看来在资料库端排除相同资料也不是很好的作法…

关键点在于怎么分辨出使用者正在重刷页面,进一步地,有没有一劳永逸的做法,让我们可以检查某一属性就能判断是不是重刷页面所回传,来避免重复送出动作?转念一想:太阳底下无新事,上网搜寻了一下,国外有几篇文章、讨论串针对这问题提出了几个解法 (事实证明前述两种作法也是有人建议),其中我觉得最值得一看的是底下两篇:

  • Build Your ASP.NET Pages on a Richer Bedrock
  • Preventing Duplicate Record Insertion on Page Refresh

    参考上列两篇文章的内容,得到最后的答案是:我们可以继承 ASP.NET 的 Page 类别,自行扩充所需的功能!作法如下:

    1、继承 System.Web.UI.Page,自订一个 BasePage 类别。

    以下为引用的内容:
    1. using System;   
    2.   
    3. /// <summary>   
    4. /// BasePage 的摘要描述   
    5. /// </summary>   
    6. public class BasePage : System.Web.UI.Page   
    7. {   
    8.     public BasePage() { }   
    9. }  

    2、在 BasePage 类别底下撰写 SetActionStamp 方法,目的是在 Session 存放一个系统时间戳记。

    以下为引用的内容:
    1. /// <summary>   
    2. /// 設置戳記   
    3. /// </summary>   
    4. private void SetActionStamp()   
    5. {   
    6.     Session["actionStamp"] = Server.UrlEncode(DateTime.Now.ToString());   
    7. }  

    3、处理 PreRender 事件,在网页初次载入时设置戳记,且每次载入执行时会把该戳记存放到 HiddenField 里。

    以下为引用的内容:
    1. public BasePage() { this.PreRender += new EventHandler(Page_PreRender); }   
    2.   
    3. void Page_PreRender(object sender, EventArgs e)   
    4. {   
    5.     if (!IsPostBack)   
    6.     {   
    7.         SetActionStamp();   
    8.     }   
    9.   
    10.     ClientScript.RegisterHiddenField("actionStamp", Session["actionStamp"].ToString());   
    11. }  

    4、自订 IsRefresh 属性,藉由判断 HiddenField 存放的戳记是否等于 Session 裡存放的值,就可以得知网页是否经由重新整理动作回传。

    以下为引用的内容:
    1. /// <summary>   
    2. /// 取得值,指出网页是否经由重新整理动作回传 (PostBack)   
    3. /// </summary>   
    4. protected bool IsRefresh   
    5. {   
    6.     get  
    7.     {   
    8.         if HttpContext.Current.Request["actionStamp"] as string == Session["actionStamp"] as string)   
    9.         {   
    10.             SetActionStamp();   
    11.             return false;   
    12.         }   
    13.   
    14.         return true;   
    15.     }   
    16. }  

    之后撰写网页程序时,只要衍生自 BasePage 就可以取得 IsRefresh 属性值,可以用来判断网页是否被重新整理,避免重复执行之前的动作:

    至于概念是这样:网页初次载入时我们在 Session 记录时间戳记,Copy 一份到 HiddenField 存放起来,在页面反覆执行时该戳记始终是初次设置的值,直到某一项动作我们希望可以辨别是否经由重新整理所送出,所以对 IsRefresh 属性加以判断 ,初次送出时当然会回传 false,作业可以顺利执行,连带只更新 Session 裡的时间戳记,这时已不同 HiddenField 裡的值。有趣的事情来了,Refresh 时把前次动作再送出一次,但因为浏览器会 Cache 状态,这时 HiddenField 裡的时间戳记依然是较旧的值,不同于 Session 所持有的,检查 IsRefresh 属性值是 true,为了避免重复执行就可以把动作挡下来。

    目前为止,本文就标题所提的问题提出解法并简单说明了概念,但前面所提供的那两篇参考文章其实有详细的阐述,想了解的人建议一定要去看看,特别是第一篇由大师 Dino Esposito(介紹、個人部落格) 所写的文章。

    最后说一个 Dino 大师的文章有提到的小技巧,既然我们扩充了一个有侦测页面重新整理功能的 BasePage 类别,那要怎么让之后新加入的 Web Form 预设都是由 BasePage 衍生而来?可以打开 web.config 档裡 <system.web》 底下的 <pages》 设定 pageBaseType 属性,例如:

    设定完成之后,往后新加入的 Web Form 就会改继承自 BasePage。

时间: 2025-01-26 20:23:46

ASP.NET 如何避免页面重新整理时重复送出的相关文章

急问: web自定义控件设置了自定义属性以后,页面的设计时支持就出错了

问题描述 请问解决办法 解决方案 解决方案二:补充一句:此问题只发生在有updatepanel的页面中解决方案三:ding~~~~~~~~~~~~解决方案四:报什么错呢?问一个不痛不痒的问题,谁知道怎么解答啊

ASP生成html静态页面

原理:通过浏览器传送变量,如 http://127.0.0.1/shengcheng.asp?id=90 代码: if SaveFile("/new/"&id&".html","http://127.0.0.1/news.asp?id="&id&"") then 中 /new"&id&".html",是你生成的文件和路径.http://127.0.0

实战ASP(6):使用WML和ASP编写动态手机页面

动态|页面 使用WML和ASP编写动态手机页面      ASP与WAP结合能够生成丰富的动态的WML网页,可以给WAP手机带来许多有趣的内容.那么如何使他们两者有机的结合来生成动态网页呢?在本文中,我就想介绍一下如何使用ASP开发一个动态生成WML的应用程序.比如说,现在手机的电子银行被炒得很火,那么我们如何去建立这样一个应用程序呢?在这里我不想讨论整个电子银行的建立,因为那样内容太庞大,超出了我能介绍的范围,在这里我只想介绍一下,如何使用ASP建立一个应用程序来检查用户的收支平衡情况.考虑到

Asp中代码与页面的分离

页面 为了避免ASP程序和HTML代码混写造成维护困难的情况,本文介绍了一种方法,利用模板来分离程序和页面,使程序设计更加轻松. 在使用ASP制作一个站点的时候,常常会出现一个ASP文件中,程序代码和HTML代码混合的情况.这样子做有许多缺点: 1. 且不说编程时就要对页面布局进行设计和编排,造成代码混乱难懂,不规范: 2. 当需要改变页面外观时,你不仅要改变HTML部份,也需要改变ASP代码,不易维护. 那么,要如何才能避免这些麻烦呢? 答案就是使用模板文件,将ASP代码和HTML页面分开,一

探讨:关于Asp中代码与页面的分离

页面 为了避免ASP程序和HTML代码混写造成维护困难的情况,本文介绍了一种方法,利用模板来分离程序和页面,使程序设计更加轻松.在使用ASP制作一个站点的时候,常常会出现一个ASP文件中,程序代码和HTML代码混合的情况.这样子做有许多缺点:1.且不说编程时就要对页面布局进行设计和编排,造成代码混乱难懂,不规范:2.当需要改变页面外观时,你不仅要改变HTML部份,也需要改变ASP代码,不易维护.那么,要如何才能避免这些麻烦呢?答案就是使用模板文件,将ASP代码和HTML页面分开,一切问题就都解决

使用WML和ASP编写动态手机页面(转)

动态|页面 ASP与WAP结合能够生成丰富的动态的WML网页,可以给WAP手机带来许多有趣的内容.那么如何使他们两者有机的结合来生成动态网页呢?在本文中,我就想介绍一下如何使用ASP开发一个动态生成WML的应用程序.比如说,现在手机的电子银行被炒得很火,那么我们如何去建立这样一个应用程序呢?在这里我不想讨论整个电子银行的建立,因为那样内容太庞大,超出了我能介绍的范围,在这里我只想介绍一下,如何使用ASP建立一个应用程序来检查用户的收支平衡情况.考虑到它的安全性,我们要使用WTLS,但这个问题比较

ASP.NET 2.0 页面状态持续程序

asp.net|程序|页面 ASP.NET 控件的开发人员利用 ViewState 和控件状态来保持浏览器发出的各请求之间的状态信息.通常,该信息作为由页面呈现的 HTML 标记中的隐藏字段传送给客户端.然后,该页面状态作为下一个窗体提交的一部分传回服务器并还原给控件或页面.即使浏览器使用 HTTP 协议(该协议定义为无状态),但利用临时存储状态信息的功能,控件的开发人员能够轻松地提供更丰富的应用程序体验. ASP.NET 2.0 允许您修改临时保持页面状态的位置和方式.在某些情况下,避免在客户

AJAX页面参数相同时只返回缓存的内容的解决办法

AJAX页面参数相同时只返回缓存的内容的解决办法 常利用AJAX写一些页面无刷新的内容获取页面,这种方式很快捷也很方便,但其中有一个问题,就是如果两次提交的参数相同时,返回的内容只返回上次获取的内容,如果我们在第一次修改了参数,第二次再次调用却会发现页面根本没有改变.这样的情况是是为AJAX获取时先检查本机缓存,如果本机缓存已有相同内容,则不访问远端服务器.这样的操作倒是可以提高速度和减少服务器压力.但带来的弊端也是显而易见的. 为了解决这个问题.我们必须在获取页加上一个额外的参数.比较简单的方

ASP.NET 2.0页面框架简要慨述

ASP.NET Web窗体页面框架组件是一个可缩放的通用语言运行时编程模型,它可以用于动态地生成Web页面.ASP.NET页面框架组件是ASP的合理进化(ASP.NET提供的语法与已有的页面是兼容的),它被特意地设计用于解决早期模型中的大量的关键的缺陷.特别是,它提供了建立和使用可供重复使用的(reusable)UI控件的能力,这些控件可以封装公用的功能,从而减少了页面开发者需要编写的代码数量:它使开发者能够清晰有序地构造页面逻辑(并非"缠绕在一起的代码"):它使用的开发工具提供了强大