webapi 自定义缓存实现

定义一个Filter

  public class MyOutputCacheAttribute : ActionFilterAttribute
    {

        MemoryCacheDefault _cache = new MemoryCacheDefault();

        /// <summary>
        /// 客户端缓存(用户代理)缓存相对过期时间
        /// </summary>
        public int ClientCacheExpiration { set; get; }

        /// <summary>
        /// 服务端缓存过期时间
        /// </summary>
        public int ServerCacheExpiration { set; get; }

        /// <summary>
        ///
        /// </summary>
        /// <param name="clientCacheExpiration">客户端过期时间。单位为秒,默认为600秒(10分钟)</param>
        /// <param name="cerverCacheExpiration">服务端过期时间。单位为秒,默认为7200秒(120分钟)</param>
        public MyOutputCacheAttribute(int clientCacheExpiration = 600, int serverCacheExpiration = 7200)
        {
            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;

            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;
        }

    }

上面的这段代码严格遵循RFC2626中定义的缓存协议。

定义一个服务器端缓存实现

这里采用MemoryCache,也可以采用memcached, redis之类的。

public class MemoryCacheDefault
{
    private static readonly MemoryCache _cache = MemoryCache.Default;

    public void RemoveStartsWith(string key)
    {
        lock (_cache)
        {
            _cache.Remove(key);
        }
    }

    public T Get<T>(string key) where T : class
    {
        var o = _cache.Get(key) as T;
        return o;
    }

    [Obsolete("Use Get<T> instead")]
    public object Get(string key)
    {
        return _cache.Get(key);
    }

    public void Remove(string key)
    {
        lock (_cache)
        {
            _cache.Remove(key);
        }
    }

    public bool Contains(string key)
    {
        return _cache.Contains(key);
    }

    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);
        }
    }

    public IEnumerable<string> AllKeys
    {
        get
        {
            return _cache.Select(x => x.Key);
        }
    }
}

将filter应用到action中

    public class ValuesController : ApiController
    {

        [MyOutputCache(10)]
        public string Get(int id)
        {
            return "value" + id.ToString();
        }
    }
时间: 2024-10-27 13:48:55

webapi 自定义缓存实现的相关文章

借鉴dubbo实现自定义缓存

自定义缓存一般基于ConcurrentMap实现,实现缓存需要注意的点是缓存容器对象 本身依赖于 static final去存储对象,样例: ConcurrentMap<String, GenericServiceCache> cacheHolder = new ConcurrentHashMap<String, GenericServiceCache>();.虚拟机会在内存加载时 有特殊的初始化,如果通过类似单例方式创建对象会导致需要同步,而同步在高并发下导致性能变差, 更好的缓

MVC 使用自定义缓存

MVC使用自定义缓存:首先我是在一个工具类库中新建一个缓存帮助类,比如这里我在Itcast.CMS.Common 类库中新建了一个CacheHelper.cs类 using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; namespace

自定义缓存Memcache 以及Cache

*****缓存存储配置项(读缓存和appsetting配直节点的问题 )***** MemCache经常用于数据不用经常改动的数据 不用每次都去数据库中读取,可以防止在系统的缓存中,缓存在内存中读取速度快,但是memcache(是可以分布的缓存)没有保存机制,服务器挂掉,memcache丢失 系统的配置节放在了appsettings配置节下  <add key=”mingzi” value=”assas” /> ConfigurationManager.appsetting[“mingzi”]

【.net 深呼吸】自定义缓存配置(非Web项目)

在前一篇烂文中,老周简单讲述了非Web应用的缓存技术的基本用法.其实嘛,使用系统默认方案已经满足我们的需求了,不过,如果你真想自己来配置缓存,也是可以的. 缓存的自定义配置可以有两种方案,一种是用代码在应用程序中配置,即实例化MemoryCache对象时,可以向构造函数传递一个NameValueCollection实例,数据结构就是key-value形式,这些配置项的名字其实就是MemoryCacheElement类的以下三个属性:CacheMemoryLimitMegabytes.Physic

MKNetworkKit自定义缓存有效时间

MKNetworkkit是一个非常好用.轻量的网络请求框架.可以支持自动缓存. 但是在某些情况下,我们要求数据可以实时被更新,比如下单之后,订单状态的刷新.商品状态的刷新等. MKNetworkkit有一个用于控制缓存有效时间的变量,叫MKNetworkKitDefaultCacheDuration.这个变量在MKNetworkKit.h中被预定义 // MKNetworkKit.h #define kMKNetworkKitDefaultCacheDuration 60 // 1 minute

Mybatis源码分析自定义缓存、分页的实现

一.缓存 我们知道,在Mybatis中是有缓存实现的.分一级缓存和二级缓存,不过一级缓存其实没啥用.因为我们知道它是基于sqlSession的,而sqlSession在每一次的方法执行时都会被新创建.二级缓存是基于namespace,离开了它也是不行.有没有一种方式来提供自定义的缓存机制呢? 1.Executor Executor是Mybatis中的执行器.所有的查询就是调用它的<E> List<E> query()方法.我们就可以在这里进行拦截,不让它执行后面的查询动作, 直接从

laravel自定义缓存memcache(自带memcached,windows不支持)

1.首先弄清楚memcache和memcached的区别(自行搜索) 2.安装memcache服务端(参照https://www.cnblogs.com/winstonsias/p/10190745.html)及php扩展(参照https://www.cnblogs.com/winstonsias/p/10193781.html) 3.添加服务提供类 MemcacheServiceProvider ,记得在app.php配置文件添加提供者注册 1 namespace App\Providers;

WebApi自定义返回类型和命名空间实现

1.自定义ContentNegotiator /// <summary> /// 返回json的ContentNegotiator /// </summary> public class JsonContentNegotiator : IContentNegotiator { private readonly JsonMediaTypeFormatter _jsonFormatter; public JsonContentNegotiator(JsonMediaTypeFormat

redis自定义缓存

1 import weakref, collections 2 import time 3 4 5 class LocalCache(): 6 notFound = object() 7 8 class Dict(dict): 9 def __del__(self): 10 pass 11 12 def __init__(self,maxlen=20): 13 self.weak = weakref.WeakKeyDictionary() 14 self.strong=collections.d