public void Set(string key, CacheItem cacheItem) { if (cacheItem.ValidFor <= 0) { return; } var value = JsonConvert.SerializeObject(cacheItem); Database.StringSet(GetLocalizedKey(key), value, TimeSpan.FromSeconds(cacheItem.ValidFor)); }
public void Set(string key, CacheItem cacheItem) { if (cacheItem.ValidFor <= 0) { return; } Logger.Debug("Set() invoked with key='{0}' in region '{1}'.", key, _regionAlphaNumeric); _cache.Put(key, cacheItem, TimeSpan.FromSeconds(cacheItem.ValidFor), _regionAlphaNumeric); }
public void Set(string key, CacheItem cacheItem) { _workContext.HttpContext.Cache.Add( key, cacheItem, null, cacheItem.ValidUntilUtc, System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null); }
public void Set(string key, CacheItem cacheItem) { _workContext.HttpContext.Cache.Add( key, cacheItem, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 0, cacheItem.ValidFor), System.Web.Caching.CacheItemPriority.Normal, null); }
public void Set(string key, CacheItem cacheItem) { lock (String.Intern(key)) { var records = _repository.Table.Where(x => x.CacheKey == key).ToList(); var record = records.FirstOrDefault(); if (record == null) { record = new CacheItemRecord(); Convert(cacheItem, record); _repository.Create(record); return; } Convert(cacheItem, record); } }
public void Set(string key, CacheItem cacheItem) { if(cacheItem == null) { throw new ArgumentNullException("cacheItem"); } if (cacheItem.ValidFor <= 0) { return; } using (var decompressedStream = Serialize(cacheItem)) { using (var compressedStream = Compress(decompressedStream)) { Database.StringSet(GetLocalizedKey(key), compressedStream.ToArray(), TimeSpan.FromSeconds(cacheItem.ValidFor)); } } }
private void Convert(CacheItem cacheItem, CacheItemRecord record) { record.CacheKey = cacheItem.CacheKey; record.CachedOnUtc = cacheItem.CachedOnUtc; record.Duration = cacheItem.Duration; record.GraceTime = cacheItem.GraceTime; record.ValidUntilUtc = cacheItem.ValidUntilUtc; record.StoredUntilUtc = cacheItem.StoredUntilUtc; record.ContentType = cacheItem.ContentType; record.InvariantCacheKey = cacheItem.InvariantCacheKey; record.Output = cacheItem.Output; record.QueryString = cacheItem.QueryString; record.StatusCode = cacheItem.StatusCode; record.Tags = String.Join(";", cacheItem.Tags); record.Tenant = cacheItem.Tenant; record.Url = cacheItem.Url; }
private CacheItem Convert(CacheItemRecord record) { var cacheItem = new CacheItem(); cacheItem.CacheKey = record.CacheKey; cacheItem.CachedOnUtc = record.CachedOnUtc; cacheItem.Duration = record.Duration; cacheItem.GraceTime = record.GraceTime; cacheItem.ContentType = record.ContentType; cacheItem.InvariantCacheKey = record.InvariantCacheKey; cacheItem.Output = record.Output; cacheItem.QueryString = record.QueryString; cacheItem.StatusCode = record.StatusCode; cacheItem.Tags = record.Tags.Split(';'); cacheItem.Tenant = record.Tenant; cacheItem.Url = record.Url; return cacheItem; }
private void ServeCachedItem(ActionExecutingContext filterContext, CacheItem cacheItem) { var response = filterContext.HttpContext.Response; // Fix for missing charset in response headers response.Charset = response.Charset; // Adds some caching information to the output if requested. if (CacheSettings.DebugMode) { response.AddHeader("X-Cached-On", cacheItem.CachedOnUtc.ToString("r")); response.AddHeader("X-Cached-Until", cacheItem.ValidUntilUtc.ToString("r")); } // Shorcut action execution. filterContext.Result = new FileContentResult(cacheItem.Output, cacheItem.ContentType); response.StatusCode = cacheItem.StatusCode; ApplyCacheControl(response); }
public void OnResultExecuted(ResultExecutedContext filterContext) { var captureHandlerIsAttached = false; try { // This filter is not reentrant (multiple executions within the same request are // not supported) so child actions are ignored completely. if (filterContext.IsChildAction || !_isCachingRequest) return; Logger.Debug("Item '{0}' was rendered.", _cacheKey); // Obtain individual route configuration, if any. CacheRouteConfig configuration = null; var configurations = _cacheService.GetRouteConfigs(); if (configurations.Any()) { var route = filterContext.Controller.ControllerContext.RouteData.Route; var key = _cacheService.GetRouteDescriptorKey(filterContext.HttpContext, route); configuration = configurations.FirstOrDefault(c => c.RouteKey == key); } if (!ResponseIsCacheable(filterContext, configuration)) { filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache); filterContext.HttpContext.Response.Cache.SetNoStore(); filterContext.HttpContext.Response.Cache.SetMaxAge(new TimeSpan(0)); return; } // Determine duration and grace time. var cacheDuration = configuration != null && configuration.Duration.HasValue ? configuration.Duration.Value : CacheSettings.DefaultCacheDuration; var cacheGraceTime = configuration != null && configuration.GraceTime.HasValue ? configuration.GraceTime.Value : CacheSettings.DefaultCacheGraceTime; // Include each content item ID as tags for the cache entry. var contentItemIds = _displayedContentItemHandler.GetDisplayed().Select(x => x.ToString(CultureInfo.InvariantCulture)).ToArray(); // Capture the response output using a custom filter stream. var response = filterContext.HttpContext.Response; var captureStream = new CaptureStream(response.Filter); response.Filter = captureStream; captureStream.Captured += (output) => { try { // Since this is a callback any call to injected dependencies can result in an Autofac exception: "Instances // cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed." // To prevent access to the original lifetime scope a new work context scope should be created here and dependencies // should be resolved from it. using (var scope = _workContextAccessor.CreateWorkContextScope()) { var cacheItem = new CacheItem() { CachedOnUtc = _now, Duration = cacheDuration, GraceTime = cacheGraceTime, Output = output, ContentType = response.ContentType, QueryString = filterContext.HttpContext.Request.Url.Query, CacheKey = _cacheKey, InvariantCacheKey = _invariantCacheKey, Url = filterContext.HttpContext.Request.Url.AbsolutePath, Tenant = scope.Resolve<ShellSettings>().Name, StatusCode = response.StatusCode, Tags = new[] { _invariantCacheKey }.Union(contentItemIds).ToArray() }; // Write the rendered item to the cache. var cacheStorageProvider = scope.Resolve<IOutputCacheStorageProvider>(); cacheStorageProvider.Remove(_cacheKey); cacheStorageProvider.Set(_cacheKey, cacheItem); Logger.Debug("Item '{0}' was written to cache.", _cacheKey); // Also add the item tags to the tag cache. var tagCache = scope.Resolve<ITagCache>(); foreach (var tag in cacheItem.Tags) { tagCache.Tag(tag, _cacheKey); } } } finally { // Always release the cache key lock when the request ends. ReleaseCacheKeyLock(); } }; captureHandlerIsAttached = true; } finally { // If the response filter stream capture handler was attached then we'll trust // it to release the cache key lock at some point in the future when the stream // is flushed; otherwise we'll make sure we'll release it here. if (!captureHandlerIsAttached) ReleaseCacheKeyLock(); } }
public void OnActionExecuting(ActionExecutingContext filterContext) { // apply OutputCacheAttribute logic if defined var actionAttributes = filterContext.ActionDescriptor.GetCustomAttributes(typeof(OutputCacheAttribute), true); var controllerAttributes = filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(OutputCacheAttribute), true); var outputCacheAttribute = actionAttributes.Concat(controllerAttributes).Cast<OutputCacheAttribute>().FirstOrDefault(); if (outputCacheAttribute != null) { if (outputCacheAttribute.Duration <= 0 || outputCacheAttribute.NoStore) { Logger.Debug("Request ignored based on OutputCache attribute"); return; } } // saving the current datetime _now = _clock.UtcNow; // before executing an action, we check if a valid cached result is already // existing for this context (url, theme, culture, tenant) Logger.Debug("Request on: " + filterContext.RequestContext.HttpContext.Request.RawUrl); // don't cache POST requests if (filterContext.HttpContext.Request.HttpMethod.Equals("POST", StringComparison.OrdinalIgnoreCase)) { Logger.Debug("Request ignored on POST"); return; } // don't cache the admin if (AdminFilter.IsApplied(new RequestContext(filterContext.HttpContext, new RouteData()))) { Logger.Debug("Request ignored on Admin section"); return; } // ignore child actions, e.g. HomeController is using RenderAction() if (filterContext.IsChildAction) { Logger.Debug("Request ignored on Child actions"); return; } _workContext = _workContextAccessor.GetContext(); // don't return any cached content, or cache any content, if the user is authenticated if (_workContext.CurrentUser != null) { Logger.Debug("Request ignored on Authenticated user"); return; } // caches the default cache duration to prevent a query to the settings _cacheDuration = _cacheManager.Get("CacheSettingsPart.Duration", context => { context.Monitor(_signals.When(CacheSettingsPart.CacheKey)); return _workContext.CurrentSite.As<CacheSettingsPart>().DefaultCacheDuration; } ); // caches the default max age duration to prevent a query to the settings _maxAge = _cacheManager.Get("CacheSettingsPart.MaxAge", context => { context.Monitor(_signals.When(CacheSettingsPart.CacheKey)); return _workContext.CurrentSite.As<CacheSettingsPart>().DefaultMaxAge; } ); _varyQueryStringParameters = _cacheManager.Get("CacheSettingsPart.VaryQueryStringParameters", context => { context.Monitor(_signals.When(CacheSettingsPart.CacheKey)); var varyQueryStringParameters = _workContext.CurrentSite.As<CacheSettingsPart>().VaryQueryStringParameters; return string.IsNullOrWhiteSpace(varyQueryStringParameters) ? null : varyQueryStringParameters.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray(); } ); var varyRequestHeadersFromSettings = _cacheManager.Get("CacheSettingsPart.VaryRequestHeaders", context => { context.Monitor(_signals.When(CacheSettingsPart.CacheKey)); var varyRequestHeaders = _workContext.CurrentSite.As<CacheSettingsPart>().VaryRequestHeaders; return string.IsNullOrWhiteSpace(varyRequestHeaders) ? null : varyRequestHeaders.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray(); } ); _varyRequestHeaders = (varyRequestHeadersFromSettings == null) ? new HashSet<string>() : new HashSet<string>(varyRequestHeadersFromSettings); // different tenants with the same urls have different entries _varyRequestHeaders.Add("HOST"); // Set the Vary: Accept-Encoding response header. // This instructs the proxies to cache two versions of the resource: one compressed, and one uncompressed. // The correct version of the resource is delivered based on the client request header. // This is a good choice for applications that are singly homed and depend on public proxies for user locality. _varyRequestHeaders.Add("Accept-Encoding"); // caches the ignored urls to prevent a query to the settings _ignoredUrls = _cacheManager.Get("CacheSettingsPart.IgnoredUrls", context => { context.Monitor(_signals.When(CacheSettingsPart.CacheKey)); return _workContext.CurrentSite.As<CacheSettingsPart>().IgnoredUrls; } ); // caches the culture setting _applyCulture = _cacheManager.Get("CacheSettingsPart.ApplyCulture", context => { context.Monitor(_signals.When(CacheSettingsPart.CacheKey)); return _workContext.CurrentSite.As<CacheSettingsPart>().ApplyCulture; } ); // caches the debug mode _debugMode = _cacheManager.Get("CacheSettingsPart.DebugMode", context => { context.Monitor(_signals.When(CacheSettingsPart.CacheKey)); return _workContext.CurrentSite.As<CacheSettingsPart>().DebugMode; } ); // don't cache ignored url ? if (IsIgnoredUrl(filterContext.RequestContext.HttpContext.Request.AppRelativeCurrentExecutionFilePath, _ignoredUrls)) { return; } var queryString = filterContext.RequestContext.HttpContext.Request.QueryString; var requestHeaders = filterContext.RequestContext.HttpContext.Request.Headers; var parameters = new Dictionary<string, object>(filterContext.ActionParameters); foreach (var key in queryString.AllKeys) { if (key == null) continue; parameters[key] = queryString[key]; } foreach (var varyByRequestHeader in _varyRequestHeaders) { if (requestHeaders.AllKeys.Contains(varyByRequestHeader)) { parameters["HEADER:" + varyByRequestHeader] = requestHeaders[varyByRequestHeader]; } } // compute the cache key _cacheKey = ComputeCacheKey(filterContext, parameters); // create a tag which doesn't care about querystring _invariantCacheKey = ComputeCacheKey(filterContext, null); // don't retrieve cache content if refused // in this case the result of the action will update the current cached version if (filterContext.RequestContext.HttpContext.Request.Headers["Cache-Control"] != "no-cache") { // fetch cached data _cacheItem = _cacheStorageProvider.GetCacheItem(_cacheKey); if (_cacheItem == null) { Logger.Debug("Cached version not found"); } } else { Logger.Debug("Cache-Control = no-cache requested"); } var response = filterContext.HttpContext.Response; // render cached content if (_cacheItem != null) { Logger.Debug("Cache item found, expires on " + _cacheItem.ValidUntilUtc); var output = _cacheItem.Output; // adds some caching information to the output if requested if (_debugMode) { response.AddHeader("X-Cached-On", _cacheItem.CachedOnUtc.ToString("r")); response.AddHeader("X-Cached-Until", _cacheItem.ValidUntilUtc.ToString("r")); } // shorcut action execution filterContext.Result = new ContentResult { Content = output, ContentType = _cacheItem.ContentType }; response.StatusCode = _cacheItem.StatusCode; ApplyCacheControl(_cacheItem, response); return; } _cacheItem = new CacheItem(); // get contents ApplyCacheControl(_cacheItem, response); // no cache content available, intercept the execution results for caching _previousFilter = response.Filter; response.Filter = _filter = new CapturingResponseFilter(); }
/// <summary> /// Define valid cache control values /// </summary> private void ApplyCacheControl(CacheItem cacheItem, HttpResponseBase response) { if (_maxAge > 0) { var maxAge = new TimeSpan(0, 0, 0, _maxAge); //cacheItem.ValidUntilUtc - _clock.UtcNow; if (maxAge.TotalMilliseconds < 0) { maxAge = TimeSpan.FromSeconds(0); } response.Cache.SetCacheability(HttpCacheability.Public); response.Cache.SetMaxAge(maxAge); } // an ETag is a string that uniquely identifies a specific version of a component. // we use the cache item to detect if it's a new one if (HttpRuntime.UsingIntegratedPipeline) { if (response.Headers.Get("ETag") == null) { response.Cache.SetETag(cacheItem.GetHashCode().ToString(CultureInfo.InvariantCulture)); } } response.Cache.SetOmitVaryStar(true); if (_varyQueryStringParameters != null) { foreach (var queryStringParam in _varyQueryStringParameters) { response.Cache.VaryByParams[queryStringParam] = true; } } foreach (var varyRequestHeader in _varyRequestHeaders) { response.Cache.VaryByHeaders[varyRequestHeader] = true; } // create a unique cache per browser, in case a Theme is rendered differently (e.g., mobile) // c.f. http://msdn.microsoft.com/en-us/library/aa478965.aspx // c.f. http://stackoverflow.com/questions/6007287/outputcache-varybyheader-user-agent-or-varybycustom-browser response.Cache.SetVaryByCustom("browser"); // enabling this would create an entry for each different browser sub-version // response.Cache.VaryByHeaders.UserAgent = true; }
private void ServeCachedItem(ActionExecutingContext filterContext, CacheItem cacheItem) { var response = filterContext.HttpContext.Response; var request = filterContext.HttpContext.Request; // Fix for missing charset in response headers response.Charset = response.Charset; // Adds some caching information to the output if requested. if (CacheSettings.DebugMode) { response.AddHeader("X-Cached-On", cacheItem.CachedOnUtc.ToString("r")); response.AddHeader("X-Cached-Until", cacheItem.ValidUntilUtc.ToString("r")); } // Shorcut action execution. filterContext.Result = new FileContentResult(cacheItem.Output, cacheItem.ContentType); response.StatusCode = cacheItem.StatusCode; // Add ETag header if (HttpRuntime.UsingIntegratedPipeline && response.Headers.Get("ETag") == null && cacheItem.ETag != null) { response.Headers["ETag"] = cacheItem.ETag; } // Check ETag in request // https://www.w3.org/2005/MWI/BPWG/techs/CachingWithETag.html var etag = request.Headers["If-None-Match"]; if (!String.IsNullOrEmpty(etag)) { if (String.Equals(etag, cacheItem.ETag, StringComparison.Ordinal)) { // ETag matches the cached item, we return a 304 filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.NotModified); return; } } ApplyCacheControl(response); }
private static MemoryStream Serialize(CacheItem item) { BinaryFormatter binaryFormatter = new BinaryFormatter(); var memoryStream = new MemoryStream(); binaryFormatter.Serialize(memoryStream, item); memoryStream.Seek(0, SeekOrigin.Begin); return memoryStream; }
/// <summary> /// Define valid cache control values /// </summary> private void ApplyCacheControl(CacheItem cacheItem, HttpResponseBase response) { if (_maxAge > 0) { var maxAge = new TimeSpan(0, 0, 0, _maxAge); //cacheItem.ValidUntilUtc - _clock.UtcNow; if (maxAge.TotalMilliseconds < 0) { maxAge = TimeSpan.FromSeconds(0); } response.Cache.SetCacheability(HttpCacheability.Public); response.Cache.SetMaxAge(maxAge); } // an ETag is a string that uniquely identifies a specific version of a component. // we use the cache item to detect if it's a new one if (response.Headers.Get("ETag") == null) { response.Cache.SetETag(cacheItem.GetHashCode().ToString(CultureInfo.InvariantCulture)); } response.Cache.SetOmitVaryStar(true); if (_varyQueryStringParameters != null) { foreach (var queryStringParam in _varyQueryStringParameters) { response.Cache.VaryByParams[queryStringParam] = true; } } // different tenants with the same urls have different entries response.Cache.VaryByHeaders["HOST"] = true; // Set the Vary: Accept-Encoding response header. // This instructs the proxies to cache two versions of the resource: one compressed, and one uncompressed. // The correct version of the resource is delivered based on the client request header. // This is a good choice for applications that are singly homed and depend on public proxies for user locality. response.Cache.VaryByHeaders["Accept-Encoding"] = true; // create a unique cache per browser, in case a Theme is rendered differently (e.g., mobile) // c.f. http://msdn.microsoft.com/en-us/library/aa478965.aspx // c.f. http://stackoverflow.com/questions/6007287/outputcache-varybyheader-user-agent-or-varybycustom-browser response.Cache.SetVaryByCustom("browser"); // enabling this would create an entry for each different browser sub-version // response.Cache.VaryByHeaders.UserAgent = true; }
public void OnActionExecuting(ActionExecutingContext filterContext) { // use the action in the cacheKey so that the same route can't return cache for different actions _actionName = filterContext.ActionDescriptor.ActionName; // apply OutputCacheAttribute logic if defined var outputCacheAttribute = filterContext.ActionDescriptor.GetCustomAttributes(typeof (OutputCacheAttribute), true).Cast<OutputCacheAttribute>().FirstOrDefault() ; if(outputCacheAttribute != null) { if (outputCacheAttribute.Duration <= 0 || outputCacheAttribute.NoStore) { Logger.Debug("Request ignored based on OutputCache attribute"); return; } } // saving the current datetime _now = _clock.UtcNow; // before executing an action, we check if a valid cached result is already // existing for this context (url, theme, culture, tenant) Logger.Debug("Request on: " + filterContext.RequestContext.HttpContext.Request.RawUrl); // don't cache POST requests if(filterContext.HttpContext.Request.HttpMethod.Equals("POST", StringComparison.OrdinalIgnoreCase) ) { Logger.Debug("Request ignored on POST"); return; } // don't cache the admin if (AdminFilter.IsApplied(new RequestContext(filterContext.HttpContext, new RouteData()))) { Logger.Debug("Request ignored on Admin section"); return; } // ignore child actions, e.g. HomeController is using RenderAction() if (filterContext.IsChildAction){ Logger.Debug("Request ignored on Child actions"); return; } _workContext = _workContextAccessor.GetContext(); // don't return any cached content, or cache any content, if the user is authenticated if (_workContext.CurrentUser != null) { Logger.Debug("Request ignored on Authenticated user"); return; } // caches the default cache duration to prevent a query to the settings _cacheDuration = _cacheManager.Get("CacheSettingsPart.Duration", context => { context.Monitor(_signals.When(CacheSettingsPart.CacheKey)); return _workContext.CurrentSite.As<CacheSettingsPart>().DefaultCacheDuration; } ); // caches the default max age duration to prevent a query to the settings _maxAge = _cacheManager.Get("CacheSettingsPart.MaxAge", context => { context.Monitor(_signals.When(CacheSettingsPart.CacheKey)); return _workContext.CurrentSite.As<CacheSettingsPart>().DefaultMaxAge; } ); _varyQueryStringParameters = _cacheManager.Get("CacheSettingsPart.VaryQueryStringParameters", context => { context.Monitor(_signals.When(CacheSettingsPart.CacheKey)); var varyQueryStringParameters = _workContext.CurrentSite.As<CacheSettingsPart>().VaryQueryStringParameters; return string.IsNullOrWhiteSpace(varyQueryStringParameters) ? null : varyQueryStringParameters.Split(new[]{","}, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray(); } ); // caches the ignored urls to prevent a query to the settings _ignoredUrls = _cacheManager.Get("CacheSettingsPart.IgnoredUrls", context => { context.Monitor(_signals.When(CacheSettingsPart.CacheKey)); return _workContext.CurrentSite.As<CacheSettingsPart>().IgnoredUrls; } ); // caches the culture setting _applyCulture = _cacheManager.Get("CacheSettingsPart.ApplyCulture", context => { context.Monitor(_signals.When(CacheSettingsPart.CacheKey)); return _workContext.CurrentSite.As<CacheSettingsPart>().ApplyCulture; } ); // caches the ignored urls to prevent a query to the settings _debugMode = _cacheManager.Get("CacheSettingsPart.DebugMode", context => { context.Monitor(_signals.When(CacheSettingsPart.CacheKey)); return _workContext.CurrentSite.As<CacheSettingsPart>().DebugMode; } ); var queryString = filterContext.RequestContext.HttpContext.Request.QueryString; var parameters = new Dictionary<string, object>(filterContext.ActionParameters); foreach(var key in queryString.AllKeys) { if (key == null) continue; parameters[key] = queryString[key]; } // compute the cache key _cacheKey = ComputeCacheKey(filterContext, parameters); // create a tag which doesn't care about querystring _invariantCacheKey = ComputeCacheKey(filterContext, null); // don't retrieve cache content if refused // in this case the result of the action will update the current cached version if (filterContext.RequestContext.HttpContext.Request.Headers["Cache-Control"] != "no-cache") { // fetch cached data _cacheItem = _cacheStorageProvider.GetCacheItem(_cacheKey); if (_cacheItem == null) { Logger.Debug("Cached version not found"); } } else { Logger.Debug("Cache-Control = no-cache requested"); } var response = filterContext.HttpContext.Response; // render cached content if (_cacheItem != null) { Logger.Debug("Cache item found, expires on " + _cacheItem.ValidUntilUtc); var output = _cacheItem.Output; /* * * There is no need to replace the AntiForgeryToken as it is not used for unauthenticated requests * and at this point, the request can't be authenticated * * // replace any anti forgery token with a fresh value if (output.Contains(AntiforgeryBeacon)) { var viewContext = new ViewContext { HttpContext = filterContext.HttpContext, Controller = filterContext.Controller }; var htmlHelper = new HtmlHelper(viewContext, new ViewDataContainer()); var siteSalt = _workContext.CurrentSite.SiteSalt; var token = htmlHelper.AntiForgeryToken(siteSalt); output = output.Replace(AntiforgeryBeacon, token.ToString()); } */ // adds some caching information to the output if requested if (_debugMode) { output += "\r\n<!-- Cached on " + _cacheItem.CachedOnUtc + " (UTC) until " + _cacheItem.ValidUntilUtc + " (UTC) -->"; response.AddHeader("X-Cached-On", _cacheItem.CachedOnUtc.ToString("r")); response.AddHeader("X-Cached-Until", _cacheItem.ValidUntilUtc.ToString("r")); } filterContext.Result = new ContentResult { Content = output, ContentType = _cacheItem.ContentType }; response.StatusCode = _cacheItem.StatusCode; ApplyCacheControl(_cacheItem, response); return; } _cacheItem = new CacheItem(); // get contents ApplyCacheControl(_cacheItem, response); // no cache content available, intercept the execution results for caching response.Filter = _filter = new CapturingResponseFilter(response.Filter); }
/// <summary> /// Define valid cache control values /// </summary> private void ApplyCacheControl(CacheItem cacheItem, HttpResponseBase response) { if (_maxAge > 0) { var maxAge = new TimeSpan(0, 0, 0, _maxAge); //cacheItem.ValidUntilUtc - _clock.UtcNow; if (maxAge.TotalMilliseconds < 0) { maxAge = TimeSpan.FromSeconds(0); } response.Cache.SetCacheability(HttpCacheability.Public); response.Cache.SetMaxAge(maxAge); } response.Cache.VaryByParams["*"] = true; response.DisableUserCache(); // keeping this examples for later usage // response.DisableKernelCache(); // response.Cache.SetOmitVaryStar(true); // an ETag is a string that uniquely identifies a specific version of a component. // we use the cache item to detect if it's a new one if (HttpRuntime.UsingIntegratedPipeline) { if (response.Headers.Get("ETag") == null) { response.Cache.SetETag(cacheItem.GetHashCode().ToString(CultureInfo.InvariantCulture)); } } if (_varyQueryStringParameters != null) { foreach (var queryStringParam in _varyQueryStringParameters) { response.Cache.VaryByParams[queryStringParam] = true; } } foreach (var varyRequestHeader in _varyRequestHeaders) { response.Cache.VaryByHeaders[varyRequestHeader] = true; } }