艾伟:彻底解决刷新重复提交问题,你还在用Response.Redirect吗?

简介: 以前也研究过,始终没找到好的办法,看了微软Msdn上的解决方案,使用后发现存在较多漏洞,考虑的情况太少,如页面加载后没有提交,始终刷新,在同一浏览器打开多个各选项卡,每个选项卡打开同一页面或不同页面,以下是我的解决方案,Codepublic class RefreshServe : System.

以前也研究过,始终没找到好的办法,看了微软Msdn上的解决方案,使用后发现存在较多漏洞,考虑的情况太少,如页面加载后没有提交,始终刷新,在同一浏览器打开多个各选项卡,每个选项卡打开同一页面或不同页面,以下是我的解决方案,

Code
public class RefreshServe : System.Web.UI.Page
    {
        
private static ILog log = LogManager.GetLogger(typeof(RefreshServe));

        
private readonly string REFRESH_TICKET_NAME = "__RefreshTicketArray";
        
private readonly string HIDDEN_FIELD_NAME = "__RefreshHiddenField";
        
private readonly string HIDDEN_PAGE_GUID = "__RefreshPageGuid";

        
/// <summary>
        
/// 为True表示页面刷新,False为正常提交
        
/// </summary>
        public bool IsPageRefreshed
        {
            
get
            {
                
if (IsPostBack && !CheckRefreshFlag())
                {
                    log.Debug(
"刷新了页面");
                    
return true;
                }
                
else
                {
                    log.Debug(
"正常提交");
                    
return false;
                }
            }
        }

        
/// <summary>
        
/// 呈现前更新标识
        
/// </summary>
        
/// <param name="e"></param>
        protected override void OnPreRender(EventArgs e)
        {
            log.Debug(
"执行OnPreRender");
            
base.OnPreRender(e);
            UpdateRefreshFlag();
        }


        
/// <summary>
        
/// 更新标识,正常提交都删除该次提交的时间,并生产当前新的时间
        
/// </summary>
        private void UpdateRefreshFlag()
        {

            
#region Cookie模式

            
//注册页面唯一标识并返回
            string pageGuid = SetCurPageGuid();

            HttpCookie cookie 
= GetRefreshTicket();
           
            
if (cookie.Values.Count > 0)
            {
                cookie.Values.Remove(pageGuid);
                log.Debug(
"当前清除的cookie变是:" + pageGuid);
            }

            
string submitTime = DateTime.Now.ToString("hhmmss.fffff");
            
//当前提交时间保存到隐藏域
            ClientScript.RegisterHiddenField(HIDDEN_FIELD_NAME, submitTime);


            log.Debug(
"即将要新增的时间:submitTime:" + submitTime + "  Guid:" + pageGuid.ToString());
            cookie.Values.Add(pageGuid, submitTime);

            log.Debug(
"UpdateRefreshFlag中当前Cookie中存在的记录数为:" + cookie.Values.Count);
            
for (int i = 0; i < cookie.Values.Count; i++)
                log.Info(
"cookie[" + cookie.Values.GetKey(i) + "]:" + cookie.Values[i]);

            Response.AppendCookie(cookie);

            
#endregion

        }


        
/// <summary>
        
/// 验证是否刷新
        
/// </summary>
        
/// <returns></returns>
        private bool CheckRefreshFlag()
        {
            
HttpCookie cookie = GetRefreshTicket();
            
string pageGuid = GetCurPageGuid();
            
if (cookie.Values.Count > 0)
            {
                
bool flag;
                
if (cookie.Values[pageGuid] != null)
                    flag 
= cookie.Values[pageGuid].IndexOf(GetCurSubmitTime()) > -1;
                
else
                    flag 
= true;//防止出现异常,总是可以提交
                if (flag)
                    log.Debug(
"提交时间存在,可以提交");
                
else
                    log.Debug(
"无效的提交时间");
                
return flag;
            }
            
return true;
        }


        
/// <summary>
        
/// 得到已保存的提交时间,没有新建,有返回
        
/// </summary>
        
/// <returns></returns>
        private HttpCookie GetRefreshTicket()
        {            

            
#region Cookie模式,返回值为Cookie

            HttpCookie cookie;
            
if (Request.Cookies[REFRESH_TICKET_NAME] == null)
            {
                cookie 
= new HttpCookie(REFRESH_TICKET_NAME);
                Response.AppendCookie(cookie);
                log.Debug(
"Cookie不存在,初始化");
            }
            
else
            {
                cookie 
= Request.Cookies[REFRESH_TICKET_NAME];

                log.Debug(
"读取已存在的Cookie,当前Cookie中存在的记录数为:" + cookie.Values.Count + "具体有如下几条:");

                
for (int i = 0; i < cookie.Values.Count; i++)
                    log.Info(
"cookie[" + cookie.Values.GetKey(i) + "]:" + cookie.Values[i]);
            }
            
return cookie;
            
#endregion
        }


        
/// <summary>
        
/// 获取当前提交时间
        
/// </summary>
        
/// <returns></returns>
        private string GetCurSubmitTime()
        {
            
string submitTime = Request.Params[HIDDEN_FIELD_NAME] == null ? "" : Request.Params[HIDDEN_FIELD_NAME].ToString();
            log.Debug(
"执行GetCurSubmitTime:submitTime为:" + submitTime);
            
return submitTime;
        }


        
/// <summary>
        
/// 设置页面唯一标识,通过Guid标识来区分每个页面自己的提交时间
        
/// </summary>
        private string SetCurPageGuid()
        {
            
string guid;
            
if (!IsPostBack)
            {
                
if (Request.Params[HIDDEN_PAGE_GUID] == null)
                {
                    guid 
= System.Guid.NewGuid().ToString();
                    log.Debug(
"SetCurPageGuid注册了一个新的标识:" + guid);
                }
                
else
                    guid 
= GetCurPageGuid();

            }
            
else
            {
                guid 
= GetCurPageGuid();               
            }

            ClientScript.RegisterHiddenField(HIDDEN_PAGE_GUID, guid);
            
return guid;
        }

 

        
/// <summary>
        
/// 得到当前页面的唯一标识
        
/// </summary>
        
/// <returns></returns>
        private string GetCurPageGuid()
        {
            
string pageGuid = Request.Params[HIDDEN_PAGE_GUID] == null ? "none" : Request.Params[HIDDEN_PAGE_GUID].ToString();
            log.Debug(
"执行GetCurPageGuid()后Page_GUID为:" + pageGuid);
            
return pageGuid;
        }

}

需要刷新判断功能时新页面只需继承该类就可,通过引用属性IsPageRefreshed识别"为真表示刷新,假则是正常提交",将数据库的操作写在
if(!IsPageRefreshed)
{
   数据库操作
}
即可,如果是刷新不会执行,代码中注释部分使用的是Session方式保存票证,因为session比较容易丢失且占内存,所以使用cookie,

目录
相关文章
|
SQL 缓存 PHP
【PHP开发专栏】PHP数据库查询优化技巧
【4月更文挑战第29天】本文探讨了PHP数据库查询优化技巧,包括数据库设计的规范化与反规范化,合理使用索引,优化查询逻辑,以及避免SELECT *。在SQL查询优化中,利用EXPLAIN分析查询、优化JOIN操作和子查询,以及改进WHERE条件。PHP层面的优化涉及预处理语句、缓存查询结果、分页查询优化和异步处理。此外,还提到了高级技术如数据库分区、读写分离和分布式数据库。通过这些方法,开发者能提升查询效率,优化应用性能和用户体验。
219 1
|
SQL 存储 关系型数据库
MySQL插入时间戳字段的值
【5月更文挑战第1天】
535 5
|
前端开发 JavaScript UED
window.print() 前端实现网页打印详解
window.print() 前端实现网页打印详解
628 0
|
SQL 关系型数据库 MySQL
MySQL 并发更新冗余索引字段导致的死锁
一 前言死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发同学都会在工作过程中遇见 。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。二 案例分析2.1 业务场景业务开发同学要做业务逻辑迁移,由A表迁移到B表,B表承担所有的类型的业务,他们的业务逻辑是:...
418 0
MySQL 并发更新冗余索引字段导致的死锁
|
关系型数据库 MySQL PHP
PHP 原生操作 Mysql 分页数据案例
PHP 原生操作 Mysql 分页数据案例
200 1
|
关系型数据库 MySQL PHP
PHP高级开发案例(1):使用MYSQL语句跨表查询无法导出全部记录的解决方案
PHP高级开发案例(1):使用MYSQL语句跨表查询无法导出全部记录的解决方案
133 0
html+css实战121-综合案例-小米产品-左右结构
html+css实战121-综合案例-小米产品-左右结构
296 0
html+css实战121-综合案例-小米产品-左右结构
|
SQL 关系型数据库 MySQL
MySql数据库Update批量更新与批量更新多条记录的不同值实现方法
MySql数据库Update批量更新与批量更新多条记录的不同值实现方法
2347 0
|
关系型数据库 MySQL PHP
PHP处理MySQL关联查询
什么关联查询? a)在MySQL中的Select语句中,我们经常会有复杂的业务需要使用join子语句,包括left join、right join、inner join、left outer join、right outer join、full join b)那么使用以上几种方式进行表查询的时候、对数据库的压力是很大的,怎么个大法呢,我们来测试一下: i.首先我们先搞一些数据 class表200w数据 user表 40w数据 ii.我们接下来查询15个班级下面的所有学生
|
SQL 关系型数据库 MySQL