Core2中使用Microsoft.AspNetCore.Mvc下的ResponseCacheAttribute特性來控制Http Get請求的緩存
原理是設置http請求 響應頭的Cache-control來告訴瀏覽器如何進行客戶端緩存
1、在Startup的ConfigureServices方法里面設置一個CacheProfiles,Duration屬性定義瀏覽器緩存的秒數,CacheProfiles一個通用的緩存配置項
services.AddMvc(option => { /*客戶端緩存*/ option.CacheProfiles.Add("default", new Microsoft.AspNetCore.Mvc.CacheProfile { Duration = 600 /*10分鍾*/ }); });
2、在需要緩存的Action上面添加ResponseCacheAttribute特性,CacheProfileName 的值使用服務配置的名稱,該Action將使用配置項進行緩存
[ResponseCache(CacheProfileName = "default")]
也可以在Action 上賦予 Duration 值,指定瀏覽器緩存的時間
查看ResponseCacheAttribute中的代碼
public unsafe IFilterMetadata CreateInstance(IServiceProvider serviceProvider) { //IL_0000: Unknown result type (might be due to invalid IL) //IL_0008: Unknown result type (might be due to invalid IL) //IL_000e: Unknown result type (might be due to invalid IL) //IL_0025: Unknown result type (might be due to invalid IL) //IL_0032: Expected Ref, but got Unknown //IL_0046: Unknown result type (might be due to invalid IL) if (serviceProvider == (IServiceProvider)0) { throw new ArgumentNullException("serviceProvider"); } IOptions<MvcOptions> requiredService = serviceProvider.GetRequiredService<IOptions<MvcOptions>>(); CacheProfile cacheProfile = null; if (this.CacheProfileName != null) { ((IDictionary)(?)requiredService.Value.CacheProfiles).TryGetValue((!0)this.CacheProfileName, ref *(!1*)(&cacheProfile)); if (cacheProfile == null) { throw new InvalidOperationException(Resources.FormatCacheProfileNotFound(this.CacheProfileName)); } } this._duration = (this._duration ?? ((cacheProfile != null) ? cacheProfile.Duration : null)); this._noStore = (this._noStore ?? ((cacheProfile != null) ? cacheProfile.NoStore : null)); this._location = (this._location ?? ((cacheProfile != null) ? cacheProfile.Location : null)); this.VaryByHeader = (this.VaryByHeader ?? ((cacheProfile != null) ? cacheProfile.VaryByHeader : null)); this.VaryByQueryKeys = (this.VaryByQueryKeys ?? ((cacheProfile != null) ? cacheProfile.VaryByQueryKeys : null)); return new ResponseCacheFilter(new CacheProfile { Duration = this._duration, Location = this._location, NoStore = this._noStore, VaryByHeader = this.VaryByHeader, VaryByQueryKeys = this.VaryByQueryKeys }); }
可以得知Action上設置-----優先級高於--CacheProfiles里面的配置
緩存最終通過ResponseCacheFilter過濾器來實現,ResponseCacheFilter 的代碼:
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Core; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.ResponseCaching; using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; namespace Microsoft.AspNetCore.Mvc.Internal { /// <summary> /// An <see cref="T:Microsoft.AspNetCore.Mvc.Filters.IActionFilter" /> which sets the appropriate headers related to response caching. /// </summary> public class ResponseCacheFilter : IResponseCacheFilter, IActionFilter, IFilterMetadata { private readonly CacheProfile _cacheProfile; private int? _cacheDuration; private ResponseCacheLocation? _cacheLocation; private bool? _cacheNoStore; private string _cacheVaryByHeader; private string[] _cacheVaryByQueryKeys; /// <summary> /// Gets or sets the duration in seconds for which the response is cached. /// This is a required parameter. /// This sets "max-age" in "Cache-control" header. /// </summary> public int Duration { get { return (this._cacheDuration ?? this._cacheProfile.Duration) ?? 0; } set { this._cacheDuration = value; } } /// <summary> /// Gets or sets the location where the data from a particular URL must be cached. /// </summary> public ResponseCacheLocation Location { get { return (this._cacheLocation ?? this._cacheProfile.Location) ?? ResponseCacheLocation.Any; } set { this._cacheLocation = value; } } /// <summary> /// Gets or sets the value which determines whether the data should be stored or not. /// When set to <see langword="true" />, it sets "Cache-control" header to "no-store". /// Ignores the "Location" parameter for values other than "None". /// Ignores the "duration" parameter. /// </summary> public bool NoStore { get { return (this._cacheNoStore ?? this._cacheProfile.NoStore) ?? false; } set { this._cacheNoStore = value; } } /// <summary> /// Gets or sets the value for the Vary response header. /// </summary> public string VaryByHeader { get { return this._cacheVaryByHeader ?? this._cacheProfile.VaryByHeader; } set { this._cacheVaryByHeader = value; } } /// <summary> /// Gets or sets the query keys to vary by. /// </summary> /// <remarks> /// <see cref="P:Microsoft.AspNetCore.Mvc.Internal.ResponseCacheFilter.VaryByQueryKeys" /> requires the response cache middleware. /// </remarks> public string[] VaryByQueryKeys { get { return this._cacheVaryByQueryKeys ?? this._cacheProfile.VaryByQueryKeys; } set { this._cacheVaryByQueryKeys = value; } } /// <summary> /// Creates a new instance of <see cref="T:Microsoft.AspNetCore.Mvc.Internal.ResponseCacheFilter" /> /// </summary> /// <param name="cacheProfile">The profile which contains the settings for /// <see cref="T:Microsoft.AspNetCore.Mvc.Internal.ResponseCacheFilter" />.</param> public ResponseCacheFilter(CacheProfile cacheProfile) { this._cacheProfile = cacheProfile; } /// <inheritdoc /> public void OnActionExecuting(ActionExecutingContext context) { //IL_0008: Unknown result type (might be due to invalid IL) //IL_0051: Unknown result type (might be due to invalid IL) //IL_00d4: Unknown result type (might be due to invalid IL) //IL_0185: Unknown result type (might be due to invalid IL) if (context == null) { throw new ArgumentNullException("context"); } if (!this.IsOverridden(context)) { if (!this.NoStore && !this._cacheProfile.Duration.get_HasValue() && !this._cacheDuration.get_HasValue()) { throw new InvalidOperationException(Resources.FormatResponseCache_SpecifyDuration("NoStore", "Duration")); } IHeaderDictionary headers = context.HttpContext.Response.Headers; ((IDictionary)(?)headers).Remove((!0)"Vary"); ((IDictionary)(?)headers).Remove((!0)"Cache-Control"); ((IDictionary)(?)headers).Remove((!0)"Pragma"); if (!string.IsNullOrEmpty(this.VaryByHeader)) { headers["Vary"] = this.VaryByHeader; } if (this.VaryByQueryKeys != null) { IResponseCachingFeature responseCachingFeature = context.HttpContext.Features.Get<IResponseCachingFeature>(); if (responseCachingFeature == null) { throw new InvalidOperationException(Resources.FormatVaryByQueryKeys_Requires_ResponseCachingMiddleware("VaryByQueryKeys")); } responseCachingFeature.VaryByQueryKeys = this.VaryByQueryKeys; } if (this.NoStore) { headers["Cache-Control"] = "no-store"; if (this.Location == ResponseCacheLocation.None) { headers.AppendCommaSeparatedValues("Cache-Control", "no-cache"); headers["Pragma"] = "no-cache"; } } else { string text = null; switch (this.Location) { case ResponseCacheLocation.Any: text = "public"; break; case ResponseCacheLocation.Client: text = "private"; break; case ResponseCacheLocation.None: text = "no-cache"; headers["Pragma"] = "no-cache"; break; } text = string.Format((IFormatProvider)CultureInfo.get_InvariantCulture(), "{0}{1}max-age={2}", (object)text, (object)((text != null) ? "," : null), (object)this.Duration); if (text != null) { headers["Cache-Control"] = text; } } } } /// <inheritdoc /> public void OnActionExecuted(ActionExecutedContext context) { } internal bool IsOverridden(ActionExecutingContext context) { //IL_0008: Unknown result type (might be due to invalid IL) if (context == null) { throw new ArgumentNullException("context"); } return Enumerable.Last<IResponseCacheFilter>(Enumerable.OfType<IResponseCacheFilter>((IEnumerable)context.Filters)) != this; } } }