using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Reflection; using System.Web.Mvc; using System.IO; using System.Web.UI; using System.Web.Caching; using System.Text; namespace Uploading.Actions { public class ActionOutputCacheAttribute : ActionFilterAttribute { // This hack is optional; I'll explain it later in the blog post private static MethodInfo _switchWriterMethod = typeof(HttpResponse).GetMethod("SwitchWriter", BindingFlags.Instance | BindingFlags.NonPublic); public ActionOutputCacheAttribute(int cacheDuration) { _cacheDuration = cacheDuration; } private int _cacheDuration; private TextWriter _originalWriter; private string _cacheKey; public override void OnActionExecuting(ActionExecutingContext filterContext) { _cacheKey = ComputeCacheKey(filterContext); CacheContainer cachedOutput = (CacheContainer)filterContext.HttpContext.Cache[_cacheKey]; if (cachedOutput != null) { filterContext.HttpContext.Response.ContentType = cachedOutput.ContentType; filterContext.Result = new ContentResult { Content = cachedOutput.Output }; } else { StringWriter stringWriter = new StringWriterWithEncoding(filterContext.HttpContext.Response.ContentEncoding); HtmlTextWriter newWriter = new HtmlTextWriter(stringWriter); _originalWriter = (TextWriter)_switchWriterMethod.Invoke(HttpContext.Current.Response, new object[] { newWriter }); } } public override void OnResultExecuted(ResultExecutedContext filterContext) { if (_originalWriter != null) // Must complete the caching { HtmlTextWriter cacheWriter = (HtmlTextWriter)_switchWriterMethod.Invoke(HttpContext.Current.Response, new object[] { _originalWriter }); string textWritten = ((StringWriter)cacheWriter.InnerWriter).ToString(); filterContext.HttpContext.Response.Write(textWritten); CacheContainer container = new CacheContainer(textWritten, filterContext.HttpContext.Response.ContentType); filterContext.HttpContext.Cache.Add(_cacheKey, container, null, DateTime.Now.AddSeconds(_cacheDuration), Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null); } } private string ComputeCacheKey(ActionExecutingContext filterContext) { var keyBuilder = new StringBuilder(); foreach (var pair in filterContext.RouteData.Values) keyBuilder.AppendFormat("rd{0}_{1}_", pair.Key.GetHashCode(), pair.Value.GetHashCode()); foreach (var pair in filterContext.ActionParameters) keyBuilder.AppendFormat("ap{0}_{1}_", pair.Key.GetHashCode(), pair.Value.GetHashCode()); return keyBuilder.ToString(); } } class CacheContainer { public string Output; public string ContentType; /// /// Initializes a new instance of the CacheContainer class. /// /// /// public CacheContainer(string data, string contentType) { Output = data; ContentType = contentType; } } }