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();
}
}
webapi 自定义缓存实现的更多相关文章
- 【.net 深呼吸】自定义缓存配置(非Web项目)
在前一篇烂文中,老周简单讲述了非Web应用的缓存技术的基本用法.其实嘛,使用系统默认方案已经满足我们的需求了,不过,如果你真想自己来配置缓存,也是可以的. 缓存的自定义配置可以有两种方案,一种是用代码 ...
- 借鉴dubbo实现自定义缓存
自定义缓存一般基于ConcurrentMap实现,实现缓存需要注意的点是缓存容器对象 本身依赖于 static final去存储对象,样例: ConcurrentMap<String, Gene ...
- MyBatis 一、二级缓存和自定义缓存
1.一级缓存 MyBatis 默认开启了一级缓存,一级缓存是在SqlSession 层面进行缓存的.即,同一个SqlSession ,多次调用同一个Mapper和同一个方法的同一个参数,只会进行一 ...
- redis学习总结-redis作为MyBatis的自定义缓存
1.RedisCache.java package com.houtai.cache; import java.util.concurrent.locks.ReadWriteLock; import ...
- 一、WebAPI自定义过滤器的使用
一.WebAPI自定义过滤器的使用 1.注册过滤器 using System.Web.Http; using KYINT.WebAPIService.Handler; namespace KYINT. ...
- (转)MyBatis 一、二级缓存和自定义缓存
1.一级缓存 MyBatis 默认开启了一级缓存,一级缓存是在SqlSession 层面进行缓存的.即,同一个SqlSession ,多次调用同一个Mapper和同一个方法的同一个参数, 只会进行一次 ...
- asp.net webapi自定义输出结果类似Response.Write()
asp.net webapi自定义输出结果类似Response.Write() [HttpGet] public HttpResponseMessage HelloWorld() { string ...
- 自定义缓存管理器 或者 Spring -- cache
Spring Cache 缓存是实际工作中非常常用的一种提高性能的方法, 我们会在许多场景下来使用缓存. 本文通过一个简单的例子进行展开,通过对比我们原来的自定义缓存和 spring 的基于注释的 c ...
- .Net Core 跨平台开发实战-服务器缓存:本地缓存、分布式缓存、自定义缓存
.Net Core 跨平台开发实战-服务器缓存:本地缓存.分布式缓存.自定义缓存 1.概述 系统性能优化的第一步就是使用缓存!什么是缓存?缓存是一种效果,就是把数据结果存在某个介质中,下次直接重用.根 ...
随机推荐
- Mybatis-spring 传统dao开发
jdbc.properties jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis?chara ...
- @controller不能被识别
记得controller的类不能起名为Controller,否则不能被识别!!!!!
- python_06 函数、全局变量与局部变量、函数递归
函数 1.函数的定义: def 函数名(参数): #解释函数的功能 代码块 返回值 函数的定义主要有如下要点: def:表示函数的关键字 函数名:函数的名称,日后根据函数名调用函数 函数体:函数中进行 ...
- intellij idea 配置gitlab ssh key
1 安装git,登录官网https://www.git-scm.com/download/ ,选择相应系统版本,下载后安装好. 公司网慢的可以用第三方的软件管家下载. 2 打开git bash,不需要 ...
- easyui datagrid自定义按钮列,即最后面的操作列
在很多时候我们要在表格的最后添加一列操作列,easyUI貌似没有提供种功能,不过没关系,我们可以自定义来实现首先是HTML部分 <table id="tt" class=&q ...
- 最小生成树求最大比率 UVALive - 5713
题目链接:https://vjudge.net/problem/UVALive-5713 题意:给出t组数据,每组数据第一行给出一个n,表示点的数量,接下来n行,每行有三个数字,分别是点的坐标x,y和 ...
- Python开发【第六篇】:面向对象
configparser模块 configparser用于处理特定格式的文件,其本质是利用open来操作文件. 文件a.txt [section1] k1 = 123 k2:v2 [section ...
- 43 【redis cluster】
有两篇文章不错,可以看下: 1,初步理解redis cluster:https://blog.csdn.net/dc_726/article/details/48552531 2,仔细理解redis ...
- [leetcode]83. Remove Duplicates from Sorted List有序链表去重
Given a sorted linked list, delete all duplicates such that each element appear only once. Example 1 ...
- 51单片机学习笔记(郭天祥版)(1)——单片机基础和点亮LED灯
关于单片机型号的介绍: STC89C52RC40C-PDIP 0721CV4336..... STC:STC公司 89:89系列 C:COMS 52(还有51,54,55,58,516,):2表示存储 ...