艾伟:彻底解决刷新重复提交问题,你还在用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,

目录
相关文章
|
25天前
|
存储 移动开发 AndFix
mPaaS常见问题之pushWindow打开url白屏过一会提示网络超时如何解决
mPaaS(移动平台即服务,Mobile Platform as a Service)是阿里巴巴集团提供的一套移动开发解决方案,它包含了一系列移动开发、测试、监控和运营的工具和服务。以下是mPaaS常见问题的汇总,旨在帮助开发者和企业用户解决在使用mPaaS产品过程中遇到的各种挑战
19 0
|
1月前
|
数据采集 存储 安全
登录态数据抓取:Python爬虫携带Cookie与Session的应用技巧
登录态数据抓取:Python爬虫携带Cookie与Session的应用技巧
|
8月前
|
缓存 NoSQL JavaScript
8 种方案解决重复提交问题,还怕没有适合你的?
8 种方案解决重复提交问题,还怕没有适合你的?
Servlet学习(九):请求重定向(例如:页面搬迁后的跳转)
Servlet学习(九):请求重定向(例如:页面搬迁后的跳转)
Servlet学习(九):请求重定向(例如:页面搬迁后的跳转)
|
Web App开发 缓存 网络协议
浏览器原理 03 # HTTP 请求流程:为什么很多站点第二次打开速度会很快?
浏览器原理 03 # HTTP 请求流程:为什么很多站点第二次打开速度会很快?
338 0
浏览器原理 03 # HTTP 请求流程:为什么很多站点第二次打开速度会很快?
|
存储 JSON 安全
【面试高频】cookie、session、token?看完再也不担心被问了
【面试高频】cookie、session、token?看完再也不担心被问了
【面试高频】cookie、session、token?看完再也不担心被问了
|
前端开发 JavaScript UED
【查漏补缺】fetch请求是可以取消的
【查漏补缺】fetch请求是可以取消的
660 0
【查漏补缺】fetch请求是可以取消的
|
Web App开发 缓存 前端开发
谣言粉碎机 - 极短时间内发送两个Odata request,前一个会自动被cancel掉?
有时我们能在Chrome开发者工具的Network tab里观察到SAP UI5应用会发出某些状态为&quot;取消&quot;的OData请求。如下图第五个请求。 之前有一种似是而非的说法:极短时间内发送两个OData请求,则第一个会自动被cancel掉。
95 0
谣言粉碎机 - 极短时间内发送两个Odata request,前一个会自动被cancel掉?
|
缓存 NoSQL JavaScript
8 种方案解决重复提交问题!你选择哪一种呀?
1.什么是幂等 2.产生原因 3.解决方案 ①配置注解 ②实例化锁 ③AOP 切面 ④注解使用案例
|
网络协议 iOS开发
NSURLProtocol 拦截 NSURLSession 请求时body丢失问题解决方案探讨
“IP直连方案”主要在于解决DNS污染、省去DNS解析时间,通常情况下我们可以在项目中使用 NSURLProtocol 拦截 NSURLSession 请求,下面将支持 Post 请求中面临的一个挑战,以及应对策略介绍一下。
6376 0