惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
C
Check Point Blog
博客园_首页
B
Blog
D
Docker
U
Unit 42
量子位
I
InfoQ
有赞技术团队
有赞技术团队
Martin Fowler
Martin Fowler
GbyAI
GbyAI
L
LangChain Blog
云风的 BLOG
云风的 BLOG
博客园 - Franky
美团技术团队
T
The Blog of Author Tim Ferriss
阮一峰的网络日志
阮一峰的网络日志
月光博客
月光博客
Vercel News
Vercel News
Recent Announcements
Recent Announcements
雷峰网
雷峰网
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Google DeepMind News
Google DeepMind News

博客园 - 山顶洞外人

k8s部署dengine-3.3.6.9 wsl 磁盘 SQL 写法对比 关于达梦8管理工具执行多行sql语法错误问题 从C#接口的属性获取多个实现类中字段的值 idea 中无法连接 sql server 数据库,报错 [08S01] 驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接 TransactionScope 图片扩容 已将此 订阅标记为不活动,必须将其重新初始化。需要删除NoSync 订阅,然后重新创建它们 visual studio 2017 将winform 打包成 msi 安装文件 漏洞处理 阿里云物联网测试 net core3.1 + electron 9.31.2 项目初始化 delete in 事故清表 sql 脚本执行时间 分组后条件查询 一行拆多行 有关数据库锁表 order by 排序的数字异常
webapi filter
山顶洞外人 · 2020-11-11 · via 博客园 - 山顶洞外人
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.Caching;
using System.Text;
using System.Web;
using System.Web.Http.Filters;


namespace SH3H.WAP.WorkSheet.Outgoing.WebApi.Filters
{
    /// <summary>
    /// 定义过滤2s内重复提交的action
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
    public class FWActionSubmitFilterAttribute : ActionFilterAttribute
    {
        MemoryCacheHelper _cache = new MemoryCacheHelper();
        /// <summary>
        /// 客户端缓存(用户代理)缓存相对过期时间
        /// </summary>
        public int ClientCacheExpiration { set; get; }
        /// <summary>
        /// 服务端缓存过期时间
        /// </summary>
        public int ServerCacheExpiration { set; get; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="clientCacheExpiration">客户端过期时间。单位为秒,默认为600秒(10分钟)</param>
        /// <param name="serverCacheExpiration">服务端过期时间。单位为秒,默认为7200秒(120分钟)</param>
        public FWActionSubmitFilterAttribute(int clientCacheExpiration = 10, int serverCacheExpiration = 30)
        {
            this.ClientCacheExpiration = clientCacheExpiration;
            this.ServerCacheExpiration = serverCacheExpiration;
        }

        /// <summary>
        /// 判定是否用缓存中的内容,还是执行action是去取内容
        /// 注:一旦给actionContext.Response赋值了,则会使用这个值来输出响应
        /// </summary>
        /// <param name="actionContext"></param>
        public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
        {

            // ****************************************   穿透缓存 *********************************************
            // 若无缓存,则直接返回
            string cacheKey = getCacheKey(actionContext);
            if (_cache.Contains(cacheKey) == false)
                return;
            if (_cache.Contains(cacheKey + ":etag") == false)
                return;

            // ****************************************   使用缓存  *********************************************
            // 获取缓存中的etag
            var etag = _cache.Get<string>(cacheKey + ":etag");
            // 若etag没有被改变,则返回304,
            if (actionContext.Request.Headers.IfNoneMatch.Any(x => x.Tag == etag))  //IfNoneMatch为空时,返回的是一个集合对象
            {
                actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.NotModified); // 响应对象还没有生成,需要先生成 // 304 not modified
                actionContext.Response.Headers.CacheControl = GetDefaultCacheControl();
                actionContext.Response.Headers.ETag = new EntityTagHeaderValue(etag);
                return;
            }
            else //从缓存中返回响应
            {
                actionContext.Response = actionContext.Request.CreateResponse(); // 响应对象还没有生成,需要先生成
                // 设置协商缓存:etag
                actionContext.Response.Headers.ETag = new EntityTagHeaderValue(etag); //用缓存中的值(为最新的)更新它
                // 设置user agent的本地缓存
                actionContext.Response.Headers.CacheControl = GetDefaultCacheControl();

                // 从缓存中取中响应内容
                var content = _cache.Get<byte[]>(cacheKey);
                actionContext.Response.Content = new ByteArrayContent(content);
                return;
            }
        }






        /// <summary>
        ///  将输出保存在缓存中。
        /// </summary>
        /// <param name="actionExecutedContext"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override async System.Threading.Tasks.Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, System.Threading.CancellationToken cancellationToken)
        {
            // 响应对象已经生成,可以直接调用   actionExecutedContext.Response

            // 设置协商缓存:etag
            EntityTagHeaderValue etag = new EntityTagHeaderValue("\"" + Guid.NewGuid().ToString() + "\"");  // 根据http协议, ETag的值必须用引号包含起来
            actionExecutedContext.Response.Headers.ETag = etag;
            // 设置user agent的本地缓存
            actionExecutedContext.Response.Headers.CacheControl = GetDefaultCacheControl();

            // actionExecutedContext.Response.Headers.Remove("Content-Length"); // 改变了值,它会发生变化。删除它的话,后续的程序会自动地再计算

            // 保存到缓存
            string cacheKey = getCacheKey(actionExecutedContext.ActionContext);
            var contentBytes = await actionExecutedContext.Response.Content.ReadAsByteArrayAsync();

            _cache.Add(cacheKey + ":etag", etag.Tag, DateTimeOffset.Now.AddSeconds(this.ServerCacheExpiration));
            _cache.Add(cacheKey, contentBytes, DateTimeOffset.Now.AddSeconds(this.ServerCacheExpiration));

        }





        /// <summary>
        /// 默认的用户代理本地缓存配置,10分钟的相对过期时间
        /// 对应响应header中的 Cache-Control
        /// 这里设置里面的子项max-age。如:Cache-Control: max-age=3600
        /// </summary>
        /// <returns></returns>
        CacheControlHeaderValue GetDefaultCacheControl()
        {
            CacheControlHeaderValue control = new CacheControlHeaderValue();
            control.MaxAge = TimeSpan.FromSeconds(this.ClientCacheExpiration);  // 它对应响应头中的 cacheControl :max-age=10项

            return control;
        }




        /// <summary>
        /// 
        /// </summary>
        /// <param name="actionContext"></param>
        /// <returns></returns>
        string getCacheKey(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            string cacheKey = null;
         //获取POST中data
            var context = (HttpContextBase)actionContext.Request.Properties["MS_HttpContext"];
            context.Request.InputStream.Seek(0, SeekOrigin.Begin);
            using (var sr = new StreamReader(context.Request.InputStream, Encoding.UTF8, true, 1024, true))
            {
                string body = sr.ReadToEnd();
                // Logging here...

            }

      
            cacheKey = actionContext.Request.RequestUri.PathAndQuery; // 路径和查询部分,如: /api/values/1?ee=33&oo=5

            // 对路径中的参数进行重排,升序排列

            string controllerName = actionContext.ControllerContext.ControllerDescriptor.ControllerName;
            string actionName = actionContext.ActionDescriptor.ActionName;

            if (actionContext.ActionArguments.ContainsKey("id") == false)
            {
                cacheKey = controllerName + "/" + actionName;
            }
            else
            {
                string id = actionContext.ActionArguments["id"].ToString();
                cacheKey = controllerName + "/" + actionName + "/" + id;
            }

            return cacheKey;
        }

    }



    /// <summary>
    /// 缓存辅助类
    /// </summary>
    public class MemoryCacheHelper
    {
        private static readonly MemoryCache _cache = MemoryCache.Default;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        public void RemoveStartsWith(string key)
        {
            lock (_cache)
            {
                _cache.Remove(key);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public T Get<T>(string key) where T : class
        {
            var o = _cache.Get(key) as T;
            return o;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        [Obsolete("Use Get<T> instead")]
        public object Get(string key)
        {
            return _cache.Get(key);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        public void Remove(string key)
        {
            lock (_cache)
            {
                _cache.Remove(key);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Contains(string key)
        {
            return _cache.Contains(key);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="o"></param>
        /// <param name="expiration"></param>
        /// <param name="dependsOnKey"></param>
        public void Add(string key, object o, DateTimeOffset expiration, string dependsOnKey = null)
        {
            var cachePolicy = new CacheItemPolicy
            {
                AbsoluteExpiration = expiration
            };

            if (!string.IsNullOrWhiteSpace(dependsOnKey))
            {
                cachePolicy.ChangeMonitors.Add(
                    _cache.CreateCacheEntryChangeMonitor(new[] { dependsOnKey })
                );
            }
            lock (_cache)
            {
                _cache.Add(key, o, cachePolicy);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public IEnumerable<string> AllKeys
        {
            get
            {
                return _cache.Select(x => x.Key);
            }
        }
    }
}