`
webcode
  • 浏览: 5940558 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

.net 防止页面刷新重复提交

 
阅读更多

题记:

在处理页面事件时,我们会经常会碰到这样的情况:当我们在提交一项页面表单时,在提交成功后,当我们试图按F5刷新页面时,数据会再次的被重复提交。那么asp.net应用应用服务器是无法区别这是正常点击按钮添加还是f5刷新添加,那么这样就会导致在数据库里会存在n 条一莫一样的数据。为什么在原Asp开发程序中不会碰到这样的问题呢?我觉得是因为Asp程序主要都是将表单提交给另外一个页面处理,并且,这个页面处理之后,将跳转到另外一个提示页面。那么在Asp程序中,只需要在回退时将页面设置为过期那么就可以有效的避免重复提交的问题。但是在Asp.Net中,基本上所有的操作都是基于事件操作,而事件的本质上就是页面自己提交给自己,并且页面无法识别提交时正常操作还是重复刷新。

在我开发的第一个Web应用程序中是在项目后期,也就是基本上快完成时发现了这个问题,项目后期整个的页面框架都已经非常稳定,这时候来修改整个框架是得不偿失的,只能采取变通的方式解决这个问题。幸好整个系统的数据访问层全部都是通过存储过程来处理数据的,那么我们根据几个基本的关键字段判断这条数据是否在表里存在,如果存在那么就抛出异常(主要是Insert的存储过程),通过存储过程的判断来判断这条数据是否是重复提交的数据。这个只是权宜方案,并不是一个很好的方案,因为这样只对实体表有用,但是对关系表的插入可能很难判断,关系表主要存储的是别的表的主键,如果多个他表主键在逻辑上形成了唯一性,这样还比较好判断,但是如果多个他表主键不能在逻辑上形成唯一性,那么对唯一性的判断将十分的困难。并且实体表在某些情况下也无法适用,如果某个实体表允许除主键字段,其他字段都允许相同,那么这个也是无法判断是否是重复提交的数据。
那么如何解决这个问题呢?在微软Msdn上提供了一套解决方案,这个方案是意大利的Dino Esposito提供的。他的思想就是在客户端保存一个标志,在服务端也保存一个标志,在提交时对比两个标志的值,来判断是否是重复提交。
先看下面代码,首先是一个RefreshAction静态类,这个类主要是用来初始化服务端Session保存上一次票证的值并且对比客户端和服务端票证的值,当检测到刷新不是重复刷新时,将要把客户端的票证值更新到服务端

下面是一个HttpModule类,在请求开始时就来检测双方的票证值


下面是继承于Page页面的基类,它主要用来保存刷新的次数和客户端票证的值,并且提供一个属性来标志此页面是否是重复提交的页面


下面是一个继承于MyBasePage类的页面,它通过判断是否是重复刷新属性来显示相应的值

在WebConfig的system.web节点下加入处理请求的HttpModule

上面的代码就是解决重复刷新的代码,那么我们来分析这个代码,当我们进入页面然后点击Button1是怎么来处理这些刷新的信息的。

当我们进入页面时按照下面的顺序来执行

1、当第一次进入页面,首先由系统自动调用RefreshModule的Init事件,在此事件里,我们给Application对象的请求关联状态事件

(AcquireRequestState)注册了一个事件处理器(OnAcquireRequestState),那么当我们请求关联状态时会自动调用

OnAcquireRequestState函数。

2、调用MyBasePage的构造函数,在此函数里注册了PreRender的事件处理器。

3、第一次进入页面也是一个关联请求,现在会自动调用OnAcquireRequestState事件处理器

4、在OnAcquireRequestState事件处理器中我们调用静态类RefreshAction静态类的Check方法,HttpContext作为参数传入

1. 在Check方法里,我们首先初始化服务端票证(保存在Session里),让服务端票证的值为0。
然后我们得到上一次刷新的票证值也就是服务端票证值,它为0。
2. 我们得到这次请求保存在隐藏域里的当前的票证值,因为这是第一次请求,那么这个值为空,转换为整数,为0
3. 对比两个票证,如果当前的票证值大于上一次的票证值或者当前票证值等于上一次票证值,并且两者都为0(这表明是第一次

刷新),那么我们将当前的票证值保存为上一次票证值。这时候,客户端和服务端的票证值都为0。将标志页面是否是重复刷新的值

设置为false。如果对比条件为假,那么设置重复刷新的值为 true。
4. OnAcquireRequestState事件处理完毕

5、现在触发了PreRender事件,在页面呈现之前触发,此事件调用SaveRefreshState用来保存客户端当前票证的值。

1. 首先将刷新次数的值得到并加1,此时刷新次数为0。
2. 将刷新次数的加1的值保存到客户端当前票证的隐藏域中,那么现在当前票证的值为1,上一次票证值为0,刷新次数的值为0。

6、当我们点击Button1按钮的时候首先调用MyBasePage的构造函数注册PreRender的事件处理器
7、然后系统自动调用AcquireRequestState事件处理器,调用RefreshAction的Check方法

1. 初始化服务端票证函数无用,因为服务端票证已经存在值
2. 得到上一此票证刷新的值为0
3. 得到当前票证刷新的值为1
4. 判断票证,这时当前票证值是大于上次票证的值,将当前票证的值更新到上一次票证值,此时上一次票证值为1
5. 设置是否重刷新标志为false,这时候当前票证为1,上一次票证为1。

8、这时候不是调用PreRender事件,而是调用Button1的Click事件。

1. 判断MyBasePage的IsPageRefresh属性是否为真,很显然,现在这个值为假
2. 那么调用MyBasePage的TrackRefreshState方法,在这个方法里将刷新次数加1,保存在Session里。注意此时当前票证为1,上一

次票证为1,刷新次数为1。

9、那么这时候调用PreRender事件处理器的SaveRefreshState方法

1. 将刷新次数加1,并且保存到当前票证里,那么这时候当前票证为2,上一次票证为1,刷新次数为1。


那么我们可以观察到正常的提交服务端(上一次)票证始终小于客户端(当前)票证,刷新次数也小于当前票证,那么如果是按F5刷新呢

?我们观察一下代码


1、调用MyBasePage的构造函数注册PreRender事件

2、调用AcquireRequestState事件处理器里的Check方法

1. 初始化服务端票证,此时无效
2. 得到上一次刷新的票证为1,得到当前的票证也为1
3. 判断两个票证,此时肯定为假,那么设置重复刷新标志为false

3、处理button1的Click事件

判断IsPageRefresh属性,显然此时重复刷新标志为true,表明此次刷新是按F5刷新的。
在这里很奇怪,在正常点击时,当前票证(客户端)为2,上一次票证(服务端)为1,刷新次数为1,那么为什么按F5刷新以后,当

前票证为1了?

我刚开始也很奇怪,然后我做了一个实验,使一个按钮点击时增加隐藏域的值,让他加1,在Page_Load的时候去读取这个隐藏域,我

点击button让隐藏域的值增加,但是当我按F5时,隐藏域的值始终保持不变,那么我猜测,按F5时,不是将当前页面的数据提交给服

务端,是将缓存的数据提交给服务端,所以我们捕获到的数据值就是上一次正常提交的数据,此时隐藏域的值仍然保存最新的票证值

,但是按F5,这个值不会提交给服务器。,直到正常的点击Button1提交数据。

那么回退/前进可以说更好理解,我回退之后再点击Button1,此时提交的是上一个页面的隐藏域的值,但是存在Session里上一个票

证的值已经增加了,那么对比的时候就可以知道这是重复刷新提交的操作。

上面是这个解决方案件的原理已经阐述完毕。但是这个解决方案仍然有一定的缺点。如回退之后第一次点击可以探明是重复提交,但

是第二次点击仍然会说明是正常提交。还有一个缺点,服务端票证保存在Session里,Session是会过期的,这时候应该加一个

Session超时的判断。还有一个最大的缺点,此解决方案不能配合IFrame使用,因为在IFrame中,客户端页面会加载两次(即IFrame

外的父窗口和IFrame导向的子窗口),导致客户端票证与服务端票证相同,那么在IFrame中,提交始终是重复提交。

在实际应用中,我们肯定不能像示例那样使用这个解决方案。因为我们在项目中经常会使用用户控件,一般我们是将Button和文本框

包装成一个用户控件,点击Button抛出一个事件,由页面处理。这样还比较好判断页面是否是重复提交的。但是如果在Button在不抛

出事件,就在用户控件里自行解决,那么这样比较难以实现在事件中处理和判断页面是否重复提及。我认为这个判断最好放在

Page_Load事件里,如果是重复刷新的就跳转到另外一个提示页面(中断button的处理器),然后在跳转回来,作为第一次进入这个

页面。这样就可以避免在每次提交事件来做页面是否是刷新页面的判断。

在这里我觉得需要回顾一下Page的加载顺序。

1. Page的构造函数
2. protected void Page_PreInit(object sender, EventArgs e)
3. protected void Page_Init(object sender, EventArgs e)
4. protected void Page_InitCompleted(object send, EventArgs e)
5. protected void Page_PreLoad(object sender, EventArgs e)
6. protected void Page_Load(object sender, EventArgs e)
7. 处理完Page_Load事件,如果有提交事件就开始处理提交事件,在处理完提交事件之后在处理剩下的Page事件
8. protected void Page_LoadComplete(object sender, EventArgs e)
9. protected void Page_PreRender(object sender, EventArgs e)
10. protected void Page_PreRenderComplete(object sender, EventArgs e)
11. protected void Page_SaveStateComplete(object sender, EventArgs e)

分享到:
评论

相关推荐

    ASP.NET中防止刷新页面造成表单重复提交

    ASP.NET中防止刷新页面造成表单重复提交

    防止页面的重复提交和刷新

    防止页面的重复提交和刷新,详细实例和注解

    Asp.Net防止刷新重复提交数据的办法

    其中有一篇是来自MSDN上的一种解决方法: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvs05/html/BedrockASPNET.asp 它是通过重新定义 System.Web.UI.Page 类来实现加载页面时,是“刷新”...

    ASP.NET防止页面刷新的两种解决方法小结

    第二方法: SubmitOncePage:解决刷新页面造成的数据重复提交问题(网上资料) 执行过postback操作的web页面在刷新的时候,浏览器会有“不重新发送信息,则无法刷新网页”的提示,若刚刚执行的恰好是往数据

    防止表单重复提交(asp.net )

    防止表单重复提交。判断是新打开的页面还是刷新的页面 判断是新打开的页面还是刷新的页面

    ASP.NET中防止页面刷新造成表单重复提交执行两次操作

    之前看过别人防刷新的方法,是让页面刷新或返回上一步让页面过期,这里介绍一种另类的方法,使用Session来处理。 实现原理: 由于刷新提交表单,实际上提交的就是上一次正常提交的表单,所以我们只要做一个标志,...

    .NET 刷新页面防止表单二次提交的实现方法

    1、页面上按钮是服务器控件,现在刷新页面要防止按钮事件重复执行 您可能感兴趣的文章:ASP.NET中防止页面刷新造成表单重复提交执行两次操作asp.net 处理F5刷新页面重复提交页面的一个思路

    菜干ASP.NET防刷新控件 v1.1

    《菜干ASP.NET防刷新控件》用于防止用户向页面后台提交表单等数据后,误操作刷新页面导致数据重复提交,能应用与C#.net的winform或webform,只需要简单的引用就可避免编写大量的语句来实现该功能。 引用步骤: 1、...

    asp.net知识库

    SubmitOncePage:解决刷新页面造成的数据重复提交问题 SharpRewriter:javascript + xml技术利用#实现url重定向 采用XHTML和CSS设计可重用可换肤的WEB站点 asp.net的网址重定向方法的比较:面向搜索引擎友好 也谈 ...

    We7 CMS 2.8.2 32位安装包.zip

    7、改写OnLoad为OnInit,防止前台页面重写OnLoad造成权限丢失。 8、后台列表删除一条记录后,自动刷新列表。 9、修正工作量统计,按用户、栏目查询时,时间选择无效的问题。 10、修正任意文件下载bug。 11、内容...

    ExtAspNet_v2.3.2_dll

    -为按钮增加DisableControlBeforePostBack属性 - 回发之前是否禁用按钮,防止重复提交 - 默认为true。 -Grid的Values属性访问限制由internal改为public,这就意味这可以自由改变Grid中每个单元格的值了。 -增加...

    ExtAspNet v2.2.1 (2009-4-1) 值得一看

    -为按钮增加DisableControlBeforePostBack属性 - 回发之前是否禁用按钮,防止重复提交 - 默认为true。 -Grid的Values属性访问限制由internal改为public,这就意味这可以自由改变Grid中每个单元格的值了。 -增加...

    JavaScript网页特效范例宝典源码

    实例080 防止表单重复提交 124 实例081 自动提交表单 125 实例082 通过for循环获取表单元素的中文名称 126 实例083 可以提交到不同处理页的表单 127 第3章 实用JavaScript函数 129 3.1 数据验证 130 实例084 通过...

    CMS内容管理v2.8源码2012811

    7、改写OnLoad为OnInit,防止前台页面重写OnLoad造成权限丢失。 8、后台列表删除一条记录后,自动刷新列表。 9、修正工作量统计,按用户、栏目查询时,时间选择无效的问题。 10、修正任意文件下载bug。 11、内容模型...

    We7 CMS内容管理系统软件 网站内容管理系统 文章发布系统 新闻管理系统

    7、改写OnLoad为OnInit,防止前台页面重写OnLoad造成权限丢失。 8、后台列表删除一条记录后,自动刷新列表。 9、修正工作量统计,按用户、栏目查询时,时间选择无效的问题。 10、修正任意文件下载bug。 11、内容模型...

    LearnSite(swfupload版) 1.2.0.1 20120708b.rar

    用web方式,应该比电子教室分发任务方便,学生提交作业后管理比电子教室方便(比ftp提交作业安全,防止个别学生上传别人作业) 作业评价:学生可以互评,教师后台可以再评。 学生签到:可以了解每节课学生到场情况 ...

    ASP200问.EXE

    79.如何实现不刷新页面筛选数据库中的数据 80.如何实现公共的数据分页模块 82.如何在ADO中调用SQL函数 83.如何调用SQL Server存储过程 86.如何用ASP备份和恢复SQL Server数据库 88.如何将图像存入到数据库中 89.如何...

Global site tag (gtag.js) - Google Analytics