介绍
本文将介绍如何在asp.net web api中利用过滤器属性实现缓存。
实现过程
1,首先在web.config文件下appsettings下定义“CacheEnabled”和“CacheTimespan”两个属性,
CacheEnabled属性决定是否启用缓存
CacheTimespan决定缓存过期时间戳
如下代码所示:
<appSettings> <!--<add key="webpages:Version" value="2.0.0.0" /> <add key="webpages:Enabled" value="false" /> <add key="PreserveLoginUrl" value="true" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" />--> <add key="CacheEnabled" value="true"/> <add key="CacheTimespan" value="12000"/> </appSettings>
2,添加WebApiOutputCacheAttribute类并继承ActionFilterAttribute ,需要添加引用:
using System.Net.Http; using System.Web.Configuration; using System.Net.Http.Headers; using System.Net.Http.Formatting;
代码相当简单和直白,就是在get方法执行前判断缓存key存在不存在,如果存在读取value,并输出,在过滤器执行完后更新缓存,如果不存在刚直接调用api中get方法,输出结果。
public class WebApiOutputCacheAttribute :System.Web.Http.Filters.ActionFilterAttribute { // cache length in seconds private int _timespan; // true to enable cache private bool _cacheEnabled = false; // true if the cache depends on the caller user private readonly bool _dependsOnIdentity; // cache repository private static readonly ObjectCache WebApiCache = MemoryCache.Default; //private readonly SecurityHelper _securityHelper; private readonly bool _invalidateCache; /// <summary> /// Constructor /// </summary> public WebApiOutputCacheAttribute() : this(true) { } /// <summary> /// Constructor /// </summary> /// <param name="dependsOnIdentity"></param> public WebApiOutputCacheAttribute(bool dependsOnIdentity) : this(dependsOnIdentity, false) { } /// <summary> /// Constructor /// </summary> /// <param name="dependsOnIdentity"></param> /// <param name="invalidateCache">true to invalidate cache object</param> public WebApiOutputCacheAttribute(bool dependsOnIdentity, bool invalidateCache) { //_securityHelper = new SecurityHelper(); _dependsOnIdentity = dependsOnIdentity; _invalidateCache = invalidateCache; ReadConfig(); } /// <summary> /// Called by the ASP.NET MVC framework before the action method executes. /// </summary> /// <param name="filterContext">The filter context.</param> public override void OnActionExecuting(HttpActionContext filterContext) { if (_cacheEnabled) { if (filterContext != null) { if (IsCacheable(filterContext)) { string _cachekey = string.Join(":", new string[] { filterContext.Request.RequestUri.OriginalString, filterContext.Request.Headers.Accept.FirstOrDefault().ToString(), }); //if (_dependsOnIdentity) // _cachekey = _cachekey.Insert(0, _securityHelper.GetUser()); if (WebApiCache.Contains(_cachekey)) { //TraceManager.TraceInfo(String.Format("Cache contains key: {0}", _cachekey)); var val = (string)WebApiCache.Get(_cachekey); if (val != null) { filterContext.Response = filterContext.Request.CreateResponse(); filterContext.Response.Content = new StringContent(val); var contenttype = (MediaTypeHeaderValue)WebApiCache.Get(_cachekey + ":response-ct"); if (contenttype == null) contenttype = new MediaTypeHeaderValue(_cachekey.Split(':')[1]); filterContext.Response.Content.Headers.ContentType = contenttype; return; } } } } else { throw new ArgumentNullException("actionContext"); } } } /// <summary> /// Called by the ASP.NET MVC framework after the action method executes. /// </summary> /// <param name="filterContext">The filter context.</param> public override void OnActionExecuted(HttpActionExecutedContext filterContext) { try { if (_cacheEnabled) { if (WebApiCache != null) { string _cachekey = string.Join(":", new string[] { filterContext.Request.RequestUri.OriginalString, filterContext.Request.Headers.Accept.FirstOrDefault().ToString(), }); //if (_dependsOnIdentity) // _cachekey = _cachekey.Insert(0, _securityHelper.GetUser()); if (filterContext.Response != null && filterContext.Response.Content != null) { string body = filterContext.Response.Content.ReadAsStringAsync().Result; if (WebApiCache.Contains(_cachekey)) { WebApiCache.Set(_cachekey, body, DateTime.Now.AddSeconds(_timespan)); WebApiCache.Set(_cachekey + ":response-ct", filterContext.Response.Content.Headers.ContentType, DateTime.Now.AddSeconds(_timespan)); } else { WebApiCache.Add(_cachekey, body, DateTime.Now.AddSeconds(_timespan)); WebApiCache.Add(_cachekey + ":response-ct", filterContext.Response.Content.Headers.ContentType, DateTime.Now.AddSeconds(_timespan)); } } } } if (_invalidateCache) { CleanCache(); } } catch (Exception ex) { //TraceManager.TraceError(ex); } } /// <summary> /// Removes all items from the cache /// </summary> private static void CleanCache() { if (WebApiCache != null) { List<string> keyList = WebApiCache.Select(w => w.Key).ToList(); foreach (string key in keyList) { WebApiCache.Remove(key); } } } private void ReadConfig() { if (!Boolean.TryParse(WebConfigurationManager.AppSettings["CacheEnabled"], out _cacheEnabled)) _cacheEnabled = false; if (!Int32.TryParse(WebConfigurationManager.AppSettings["CacheTimespan"], out _timespan)) _timespan = 1800; // seconds } private bool IsCacheable(HttpActionContext ac) { if (_timespan > 0) { if (ac.Request.Method == HttpMethod.Get) return true; } else { throw new InvalidOperationException("Wrong Arguments"); } return false; } }
在api中:
// GET api/values/5 [WebApiOutputCacheAttribute(true)] public string Get(int id) { return DateTime.Now.ToString(); }