ASP.NET Core 中的響應快取中介軟體
阿新 • • 發佈:2020-12-12
客戶端(瀏覽器)快取
通過設定HTTP的響應頭來完成
1、直接用Response物件去設定
[HttpGet] public IEnumerable<WeatherForecast> Get() { Console.WriteLine("服務響應"); //直接一,簡單粗暴,不要拼寫錯了就好~~ Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.CacheControl] = "public, max-age=600";View Codevar rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray(); }
檢視http響應頭
上面的示例設定客戶端快取600秒,如果直接重新整理瀏覽器或者按F5進行重新整理,快取會失效(cache-control對重新整理無效)
2、用ResponseCacheAttribute類設定快取
[HttpGet] [ResponseCache(Duration = 100)] public IEnumerable<WeatherForecast> Get() { Console.WriteLine("服務響應"); ////直接一,簡單粗暴,不要拼寫錯了就好~~View Code//Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.CacheControl] = "public, max-age=600"; var rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray(); }
效果和上面是一致的,通過原始碼分析發現ResponseCacheAttribute也是通過設定http頭來實現的。
/// <summary> /// Specifies the parameters necessary for setting appropriate headers in response caching. /// </summary> [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class ResponseCacheAttribute : Attribute, IFilterFactory, IOrderedFilter { /// <inheritdoc /> public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) { if (serviceProvider == null) { throw new ArgumentNullException(nameof(serviceProvider)); } var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>(); var optionsAccessor = serviceProvider.GetRequiredService<IOptions<MvcOptions>>(); var cacheProfile = GetCacheProfile(optionsAccessor.Value); // ResponseCacheFilter cannot take any null values. Hence, if there are any null values, // the properties convert them to their defaults and are passed on. return new ResponseCacheFilter(cacheProfile, loggerFactory); } }View Code
ResponseCacheFilter部分程式碼如下
/// <summary> /// An <see cref="IActionFilter"/> which sets the appropriate headers related to response caching. /// </summary> internal class ResponseCacheFilter : IActionFilter, IResponseCacheFilter { /// <inheritdoc /> public void OnActionExecuting(ActionExecutingContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } // If there are more filters which can override the values written by this filter, // then skip execution of this filter. var effectivePolicy = context.FindEffectivePolicy<IResponseCacheFilter>(); if (effectivePolicy != null && effectivePolicy != this) { _logger.NotMostEffectiveFilter(GetType(), effectivePolicy.GetType(), typeof(IResponseCacheFilter)); return; } _executor.Execute(context); } }View Code
具體的實現是在ResponseCacheFilterExecutor類中,程式碼如下
internal class ResponseCacheFilterExecutor { public void Execute(FilterContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (!NoStore) { // Duration MUST be set (either in the cache profile or in this filter) unless NoStore is true. if (_cacheProfile.Duration == null && _cacheDuration == null) { throw new InvalidOperationException( Resources.FormatResponseCache_SpecifyDuration(nameof(NoStore), nameof(Duration))); } } var headers = context.HttpContext.Response.Headers; // Clear all headers headers.Remove(HeaderNames.Vary); headers.Remove(HeaderNames.CacheControl); headers.Remove(HeaderNames.Pragma); if (!string.IsNullOrEmpty(VaryByHeader)) { headers[HeaderNames.Vary] = VaryByHeader; } if (VaryByQueryKeys != null) { var responseCachingFeature = context.HttpContext.Features.Get<IResponseCachingFeature>(); if (responseCachingFeature == null) { throw new InvalidOperationException( Resources.FormatVaryByQueryKeys_Requires_ResponseCachingMiddleware(nameof(VaryByQueryKeys))); } responseCachingFeature.VaryByQueryKeys = VaryByQueryKeys; } if (NoStore) { headers[HeaderNames.CacheControl] = "no-store"; // Cache-control: no-store, no-cache is valid. if (Location == ResponseCacheLocation.None) { headers.AppendCommaSeparatedValues(HeaderNames.CacheControl, "no-cache"); headers[HeaderNames.Pragma] = "no-cache"; } } else { string cacheControlValue; switch (Location) { case ResponseCacheLocation.Any: cacheControlValue = "public,"; break; case ResponseCacheLocation.Client: cacheControlValue = "private,"; break; case ResponseCacheLocation.None: cacheControlValue = "no-cache,"; headers[HeaderNames.Pragma] = "no-cache"; break; default: cacheControlValue = null; break; } cacheControlValue = $"{cacheControlValue}max-age={Duration}"; headers[HeaderNames.CacheControl] = cacheControlValue; } } }View Code
通過原始碼分析已經知道了ResponseCacheAttribute運作的基本原理,下面再來看看如何配置出其他不同的效果。