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

目录
相关文章
|
1月前
|
数据采集 存储 安全
登录态数据抓取:Python爬虫携带Cookie与Session的应用技巧
登录态数据抓取:Python爬虫携带Cookie与Session的应用技巧
|
6月前
|
JSON 小程序 API
【uniapp小程序】request发起请求
【uniapp小程序】request发起请求
103 0
|
9月前
|
前端开发 JavaScript
|
9月前
|
前端开发 PHP
php解决ajax使用post请求时提交的数据过多而导致et::ERR_CONNECTION_RESET的解决方案
php解决ajax使用post请求时提交的数据过多而导致et::ERR_CONNECTION_RESET的解决方案
130 0
|
前端开发 JavaScript UED
【查漏补缺】fetch请求是可以取消的
【查漏补缺】fetch请求是可以取消的
664 0
【查漏补缺】fetch请求是可以取消的
|
缓存 前端开发 JavaScript
学习AJAX必知必会(3)~自动重启工具nodemon、缓存问题、请求超时和网络异常、取消重复请求
学习AJAX必知必会(3)~自动重启工具nodemon、缓存问题、请求超时和网络异常、取消重复请求
121 0
|
缓存 小程序
小程序异步请求token解决办法
小程序异步请求token解决办法
287 0
|
Web App开发 缓存 前端开发
谣言粉碎机 - 极短时间内发送两个Odata request,前一个会自动被cancel掉?
有时我们能在Chrome开发者工具的Network tab里观察到SAP UI5应用会发出某些状态为&quot;取消&quot;的OData请求。如下图第五个请求。 之前有一种似是而非的说法:极短时间内发送两个OData请求,则第一个会自动被cancel掉。
95 0
谣言粉碎机 - 极短时间内发送两个Odata request,前一个会自动被cancel掉?
|
Web App开发 前端开发 Java
SpringBoot 项目 ajax post 出现 400 Bad Request 错误 后台调试不进断点的解决方法
版权声明:本文为 testcs_dn(微wx笑) 原创文章,非商用自由转载-保持署名-注明出处,谢谢。 https://blog.csdn.net/testcs_dn/article/details/80075212 在使用Ajax的时候,你可能遇到莫名其妙的的错误,这时你需要学会使用开发者工具来查看并定位、解决问题。
2266 0
|
网络协议 iOS开发
NSURLProtocol 拦截 NSURLSession 请求时body丢失问题解决方案探讨
“IP直连方案”主要在于解决DNS污染、省去DNS解析时间,通常情况下我们可以在项目中使用 NSURLProtocol 拦截 NSURLSession 请求,下面将支持 Post 请求中面临的一个挑战,以及应对策略介绍一下。
6377 0