美文网首页一个程序员自学中
js和C# MVC架构 过滤器,防止重复提交,限流功能

js和C# MVC架构 过滤器,防止重复提交,限流功能

作者: 小船翻不翻 | 来源:发表于2020-08-28 13:26 被阅读0次

    重复提交表单拦截思路:

    1. 前端:设置一个全局变量=true,在提交表单函数开头判断,如果全局变量==false 则return 否则 设置全局变量=false;在接口回调函数中设置 全局变量=true。

           var checkCallback = true;//防止频繁点击
           //表单提交
           function submit() {
               if (!checkCallback) {
                   return;//回调函数回来前 不继续往下执行;也可以根据自己的需要给或不给用户提示
               }
               checkCallback = false;
               $.post('/SubmitContent', { }, function (res) {//这里就是回调函数
                   checkCallback = true;
                   console.log(res);
               })
           }
      
    2. 后端:找到接口参数中能表示本次请求唯一性的参数值,如:用户id;在进入接口函数前给用户打标记,接口函数响应前取消用户标记,可是使用静态变量/缓存;【如果是缓存,用户id +其他固定字符作为key,时间作为value】在接口函数入口判断,如果用户缓存标记有 则return 接口响应“频繁请求提示” 否则 给用户打标记;接口函数响应前 取消用户标记。

              /// <summary>
              /// 接口函数
              /// </summary>
              /// <param name="userId">用户id</param>
              /// <returns></returns>
              public ActionResult SubmitContent(string userId)
              {
                  string _cacheKey = $"xxxx_{userId}";
                  if (CacheHelper.GetCache(_cacheKey) != null)
                  {
                      return Json("访问过于频繁");
                  }
                  CacheHelper.SetCache(_cacheKey, DateTime.Now);
      
                  #region 业务处理代码
      
                  #endregion
      
                  CacheHelper.RemoveAllCache(_cacheKey);
      
                  return Json(null);
              }
      

    思路终归是思路,能满足基本要求,实际开发过程中,还需要对它进行丰富;如前端设置连点3次以上给出提示,后端创建一个过滤器

    再次完善,增加功能

        <script type="text/javascript">
            var checkCallback = 1;//防止频繁点击
            //表单提交
            function submit() {
                //大于1 表示上一次请求还没有返回
                if (checkCallback > 1) {
                    if (checkCallback > 3) {
                        alert('正在提交中,请稍后');
                    }
                    return;
                }
                checkCallback++;
                $.post('/SubmitContent', {}, function (res) {
                    //由于本身是异步操作,防止用于在提示前 再次点击提交;
                    setTimeout(() => { checkCallback = 1; }, 200);
                    console.log(res);
                })
            }
        </script>
    
            /// <summary>
            /// 接口函数
            /// </summary>
            /// <param name="userId">用户id</param>
            /// <returns></returns>
            public ActionResult SubmitContent(string userId)
            {
                string _cacheKey = $"xxxx_{userId}";
                var _lastTime = CacheHelper.GetCache(_cacheKey);
                if (_lastTime != null && Convert.ToDateTime(_lastTime).AddMinutes(1) > DateTime.Now)
                {
                    return Json("访问过于频繁");
                }
                CacheHelper.SetCache(_cacheKey, DateTime.Now);
    
                #region 业务处理代码
    
                #endregion
    
                CacheHelper.RemoveAllCache(_cacheKey);
    
                return Json(null);
            }
    

    我用到的是MVC架构,为了更好的服务于现有平台,再次对后端进行了封装优化

    
        /// <summary>
        /// 上次请求未响应前,不处理后来者的请求,防止重复提交
        /// 1. 默认一分钟内的请求只处理一次,超出后拦截放开
        /// 2. 最多可设置到二级参数
        /// </summary>
        public class PreventFrequentRequestAttribute : ActionFilterAttribute
        {
            public PreventFrequentRequestAttribute()
            { }
    
            #region 可配置的属性
    
            /// <summary>
            /// 接口用到的参数名称
            /// </summary>
            public string ParamsName { get; set; } = "userId";
            /// <summary>
            /// 上个接口未返回前,拦截最近多少秒的请求
            /// </summary>
            public int Seconds { get; set; } = 60;
            /// <summary>
            /// 接口响应后,是否放开拦截;可实现规定时间内的控流操作
            /// </summary>
            public bool IsClear { get; set; } = true;
            /// <summary>
            /// 是否 是一个可转JObject对象
            /// </summary>
            public bool IsJObject { get; set; } = false;
            /// <summary>
            /// 二级参数名称,配合IsJObject使用
            /// </summary>
            public string UseParamsName { get; set; } = "userId";
            #endregion
    
            /// <summary>
            /// 当前请求详细路径
            /// </summary>
            private string m_thisActionPath;
            /// <summary>
            /// 带有路径的缓存key
            /// </summary>
            private string m_paramsCacheKey;
    
            /// <summary>
            /// 从请求上下文中 获得要验证的表示参数值
            /// </summary>
            /// <param name="filterContext"></param>
            /// <returns></returns>
            public string GetParamsValue(ActionExecutingContext filterContext)
            {
                if (filterContext.ActionParameters.Count == 0 || !filterContext.ActionParameters.ContainsKey(ParamsName))
                    return null;
    
                var _paramsValue = filterContext.ActionParameters[ParamsName];
                if (IsJObject)
                {
                    return Newtonsoft.Json.Linq.JObject.FromObject(_paramsValue)?[UseParamsName]?.ToString();
                }
                return _paramsValue.ToString();
            }
    
            /// <summary>
            /// 进入处理函数前 增加访问记录
            /// </summary>
            /// <param name="filterContext"></param>
            public override void OnActionExecuting(ActionExecutingContext filterContext)
            {
                string paramsValue = GetParamsValue(filterContext);
    
                if (!string.IsNullOrWhiteSpace(paramsValue))
                {
                    m_thisActionPath = $"{filterContext.Controller}.{filterContext.ActionDescriptor.ActionName}";
                    //设置单用户频繁请求拦截
                    m_paramsCacheKey = $"{m_thisActionPath}_{paramsValue}";
                    if (CacheHelper.GetCache(m_paramsCacheKey) != null)
                    {
                        var _result = new { Code = "-10", Msg = "您的请求太频繁了,请稍后再试" };
                        filterContext.Result = new JsonResult() { Data = _result };
                        return;
                    }
                    CacheHelper.SetCache(m_paramsCacheKey, DateTime.Now, DateTime.Now.AddSeconds(Seconds));
                }
    
                base.OnActionExecuting(filterContext);
            }
            /// <summary>
            /// 离开函数前 移除访问记录
            /// </summary>
            /// <param name="filterContext"></param>
            public override void OnResultExecuting(ResultExecutingContext filterContext)
            {
                //(filterContext.Result as System.Web.Mvc.JsonResult).Data
                if (!string.IsNullOrWhiteSpace(m_paramsCacheKey) && IsClear)
                    CacheHelper.RemoveAllCache(m_paramsCacheKey);
    
                base.OnResultExecuting(filterContext);
            }
        }
    

    无论使用的哪种框架,思路都是差不多的,虽不说能直接考虑,但稍微修改下 就是可以直接使用的。

    相关文章

      网友评论

        本文标题:js和C# MVC架构 过滤器,防止重复提交,限流功能

        本文链接:https://www.haomeiwen.com/subject/zoklsktx.html