客户端(浏览器)缓存
通过设置HTTP的响应头来完成
1、直接用Response对象去设置
[HttpGet] public IEnumerable<WeatherForecast> Get() { Console.WriteLine("服务响应"); //直接一,简单粗暴,不要拼写错了就好~~ 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(); }View Code
查看http响应头
上面的示例设置客户端缓存600秒,如果直接刷新浏览器或者按F5进行刷新,缓存会失效(cache-control对刷新无效)
2、用responsecacheAttribute类设置缓存
[HttpGet] [responsecache(Duration = 100)] public IEnumerable<WeatherForecast> Get() { Console.WriteLine("服务响应"); ////直接一,简单粗暴,不要拼写错了就好~~ //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(); }View Code
效果和上面是一致的,通过源码分析发现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运作的基本原理,下面再来看看如何配置出其他不同的效果。