ASP.NET Core 中的輸出快取中介軟體
作者:Tom Dykstra
注意
這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本。
本文說明如何在 ASP.NET Core 應用程式中設定輸出快取中介軟體。 如需輸出快取的簡介,請參閱輸出快取。
輸出快取中介軟體可用於所有類型的 ASP.NET 核心應用程式:最小 API、具有控制器、MVC 和 Razor Pages 的 Web API。 範例應用程式是基本 API,但它所說明的每個快取功能在其他應用程式類型中也受到支援。
將中介軟體新增至應用程式
呼叫 AddOutputCache,將輸出快取中介軟體新增至服務集合。
呼叫 UseOutputCache,將中介軟體新增至要求處理管線。
注意
設定一個端點或頁面
針對最小 API 應用程式,請設定端點透過呼叫 CacheOutput
來執行快取,或套用 [OutputCache]
屬性,如下列範例所示:
app.MapGet("/cached", Gravatar.WriteGravatar).CacheOutput();
app.MapGet("/attribute", [OutputCache] (context) =>
Gravatar.WriteGravatar(context));
對於具有控制器的應用程式,請將 [OutputCache]
屬性套用至動作方法。 針對 Razor Pages 應用程式,將屬性套用至 Razor 頁面類別。
設定多個端點或頁面
呼叫 AddOutputCache
時建立原則,以指定套用至多個端點的快取組態。 您可以針對特定端點選取原則,而基底原則則提供端點集合的預設快取設定。
下列醒目提示的程式碼會設定所有應用程式端點的快取功能,到期時間為 10 秒。 如果未指定到期時間,則預設值為一分鐘。
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder =>
builder.Expire(TimeSpan.FromSeconds(10)));
options.AddPolicy("Expire20", builder =>
builder.Expire(TimeSpan.FromSeconds(20)));
options.AddPolicy("Expire30", builder =>
builder.Expire(TimeSpan.FromSeconds(30)));
});
下列醒目提示的程式碼會建立兩個原則,每個原則都會指定不同的到期時間。 選取的端點可以使用 20 秒的到期時間,而其他端點則可使用 30 秒的到期時間。
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder =>
builder.Expire(TimeSpan.FromSeconds(10)));
options.AddPolicy("Expire20", builder =>
builder.Expire(TimeSpan.FromSeconds(20)));
options.AddPolicy("Expire30", builder =>
builder.Expire(TimeSpan.FromSeconds(30)));
});
呼叫 CacheOutput
方法或使用 [OutputCache]
屬性時,您可以選取端點的原則:
app.MapGet("/20", Gravatar.WriteGravatar).CacheOutput("Expire20");
app.MapGet("/30", [OutputCache(PolicyName = "Expire30")] (context) =>
Gravatar.WriteGravatar(context));
對於具有控制器的應用程式,請將 [OutputCache]
屬性套用至動作方法。 針對 Razor Pages 應用程式,將屬性套用至 Razor 頁面類別。
預設輸出快取原則
根據預設,輸出快取會遵循下列規則:
- 只會快取 HTTP 200 回應。
- 只會快取 HTTP GET 或 HEAD 要求。
- 設定 cookie 的回應不會被快取。
- 已驗證要求的回應不會被快取。
下列程式碼會將所有預設快取規則套用至應用程式的所有端點:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder.Cache());
});
覆寫預設原則
下列程式碼示範如何覆寫預設規則。 下列自訂原則程式碼中反白顯示的幾行會啟用 HTTP POST 方法和 HTTP 301 回應的快取:
using Microsoft.AspNetCore.OutputCaching;
using Microsoft.Extensions.Primitives;
namespace OCMinimal;
public sealed class MyCustomPolicy : IOutputCachePolicy
{
public static readonly MyCustomPolicy Instance = new();
private MyCustomPolicy()
{
}
ValueTask IOutputCachePolicy.CacheRequestAsync(
OutputCacheContext context,
CancellationToken cancellationToken)
{
var attemptOutputCaching = AttemptOutputCaching(context);
context.EnableOutputCaching = true;
context.AllowCacheLookup = attemptOutputCaching;
context.AllowCacheStorage = attemptOutputCaching;
context.AllowLocking = true;
// Vary by any query by default
context.CacheVaryByRules.QueryKeys = "*";
return ValueTask.CompletedTask;
}
ValueTask IOutputCachePolicy.ServeFromCacheAsync
(OutputCacheContext context, CancellationToken cancellationToken)
{
return ValueTask.CompletedTask;
}
ValueTask IOutputCachePolicy.ServeResponseAsync
(OutputCacheContext context, CancellationToken cancellationToken)
{
var response = context.HttpContext.Response;
// Verify existence of cookie headers
if (!StringValues.IsNullOrEmpty(response.Headers.SetCookie))
{
context.AllowCacheStorage = false;
return ValueTask.CompletedTask;
}
// Check response code
if (response.StatusCode != StatusCodes.Status200OK &&
response.StatusCode != StatusCodes.Status301MovedPermanently)
{
context.AllowCacheStorage = false;
return ValueTask.CompletedTask;
}
return ValueTask.CompletedTask;
}
private static bool AttemptOutputCaching(OutputCacheContext context)
{
// Check if the current request fulfills the requirements
// to be cached
var request = context.HttpContext.Request;
// Verify the method
if (!HttpMethods.IsGet(request.Method) &&
!HttpMethods.IsHead(request.Method) &&
!HttpMethods.IsPost(request.Method))
{
return false;
}
// Verify existence of authorization headers
if (!StringValues.IsNullOrEmpty(request.Headers.Authorization) ||
request.HttpContext.User?.Identity?.IsAuthenticated == true)
{
return false;
}
return true;
}
}
若要使用此自訂原則,請建立具名原則:
builder.Services.AddOutputCache(options =>
{
options.AddPolicy("CachePost", MyCustomPolicy.Instance);
});
然後選取端點的具名原則:
app.MapPost("/cachedpost", Gravatar.WriteGravatar)
.CacheOutput("CachePost");
替代的預設原則覆寫
或者,使用相依性插入 (DI) 初始化實例,並對自訂原則類別進行下列變更:
- 公共建構函式,而不是私有建構函式。
- 排除自訂原則類別中的
Instance
屬性。
例如:
public sealed class MyCustomPolicy2 : IOutputCachePolicy
{
public MyCustomPolicy2()
{
}
類別的其餘部分與先前所示相同。 新增自訂原則,如下列範例所示:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder =>
builder.AddPolicy<MyCustomPolicy2>(), true);
});
上述程式碼會使用 DI 來建立自訂原則類別的實例。 建構函式中的任何公共引數都已解析。
使用自訂原則作為基底原則時,請勿在任何基底原則應套用的端點上呼叫 OutputCache()
(不含引數)。 呼叫 OutputCache()
會將預設原則新增至端點。
指定快取索引鍵
根據預設,URL 的每個部分都會包含為快取專案的索引鍵,也就是配置、主機、連接埠、路徑和查詢字串。 不過,您可能想要明確控制快取索引鍵。 例如,假設您有一個端點,其只會針對 culture
查詢字串的每個唯一值傳回唯一回應。 URL 的其他部分變化,例如其他查詢字串,不應該產生不同的快取專案。 您可以在原則中指定這類規則,如下列醒目提示的程式碼所示:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder
.With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
.Tag("tag-blog"));
options.AddBasePolicy(builder => builder.Tag("tag-all"));
options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
options.AddPolicy("NoCache", builder => builder.NoCache());
options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});
然後,您可以選取端點的 VaryByQuery
原則:
app.MapGet("/query", Gravatar.WriteGravatar).CacheOutput("Query");
以下是控制快取索引鍵的一些選項:
SetVaryByQuery - 指定要新增至快取索引鍵的一個或多個查詢字串名稱。
SetVaryByHeader - 指定要新增至快取索引鍵的一個或多個 HTTP 標頭。
VaryByValue - 指定要新增至快取索引鍵的值。 下列範例會使用值,指出目前的伺服器時間 (秒數) 是否為奇數或偶數。 只有在秒數從奇數到偶數,或從偶數到奇數時,才會產生新的回應。
app.MapGet("/varybyvalue", Gravatar.WriteGravatar) .CacheOutput(c => c.VaryByValue((context) => new KeyValuePair<string, string>( "time", (DateTime.Now.Second % 2) .ToString(CultureInfo.InvariantCulture))));
使用 OutputCacheOptions.UseCaseSensitivePaths 來指定索引鍵的路徑部分會區分大小寫。 預設值是不區分大小寫。
如需其他選項,請參閱 OutputCachePolicyBuilder 類別。
快取重新驗證
快取重新驗證表示伺服器可以傳回 304 Not Modified
HTTP 狀態碼,而不是完整的回應主體。 此狀態碼會通知用戶端,對要求的回應與先前收到的用戶端沒有變更。
下列程式碼說明如何使用 Etag
標頭來啟用快取重新驗證。 如果用戶端傳送具有先前回應之 etag 值的 If-None-Match
標頭,且快取專案是全新的,則伺服器會傳回 304 未修改,而不是完整回應:
app.MapGet("/etag", async (context) =>
{
var etag = $"\"{Guid.NewGuid():n}\"";
context.Response.Headers.ETag = etag;
await Gravatar.WriteGravatar(context);
}).CacheOutput();
執行快取重新驗證的另一種方式是檢查快取專案建立的日期,與用戶端所要求的日期相比較。 當提供要求標頭 If-Modified-Since
時,如果快取的專案較舊且未過期,輸出快取會傳回 304。
快取重新驗證是自動的,以回應從用戶端傳送的這些標頭。 除了啟用輸出快取之外,伺服器上不需要任何特殊設定才能啟用此行為。
使用標記收回快取專案
您可以使用標記來識別端點群組,並收回群組的所有快取專案。 例如,下列程式碼會建立一組端點,其 URL 開頭為「blog」,並將它們標記為「tag-blog」:
app.MapGet("/blog", Gravatar.WriteGravatar)
.CacheOutput(builder => builder.Tag("tag-blog")); ;
app.MapGet("/blog/post/{id}", Gravatar.WriteGravatar)
.CacheOutput(builder => builder.Tag("tag-blog")); ;
指派相同端點組標記的替代方式,是定義套用至開頭為 blog
之端點的基底原則:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder
.With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
.Tag("tag-blog"));
options.AddBasePolicy(builder => builder.Tag("tag-all"));
options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
options.AddPolicy("NoCache", builder => builder.NoCache());
options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});
另一個替代方法是呼叫 MapGroup
:
var blog = app.MapGroup("blog")
.CacheOutput(builder => builder.Tag("tag-blog"));
blog.MapGet("/", Gravatar.WriteGravatar);
blog.MapGet("/post/{id}", Gravatar.WriteGravatar);
在上述標籤指派範例中,這兩個端點都是由 tag-blog
標記來識別。 然後,您可以使用參考該標記的單一陳述式來收回這些端點的快取專案:
app.MapPost("/purge/{tag}", async (IOutputCacheStore cache, string tag) =>
{
await cache.EvictByTagAsync(tag, default);
});
使用此程式碼,傳送至 https://localhost:<port>/purge/tag-blog
的 HTTP POST 要求將會收回這些端點的快取專案。
您可能想要一種方式來收回所有端點的所有快取專案。 若要這樣做,請建立所有端點的基底原則,如下列程式碼所示:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder
.With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
.Tag("tag-blog"));
options.AddBasePolicy(builder => builder.Tag("tag-all"));
options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
options.AddPolicy("NoCache", builder => builder.NoCache());
options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});
此基底原則可讓您使用「全部標記」標籤來收回快取中的所有專案。
停用資源鎖定
根據預設,會啟用資源鎖定,以降低快取踩踏和驚群效應的風險。 如需詳細資訊,請參閱輸出快取。
若要停用資源鎖定,請在建立原則時呼叫 SetLocking(false),如下列範例所示:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder
.With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
.Tag("tag-blog"));
options.AddBasePolicy(builder => builder.Tag("tag-all"));
options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
options.AddPolicy("NoCache", builder => builder.NoCache());
options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});
下列範例會選取端點的無鎖定原則:
app.MapGet("/nolock", Gravatar.WriteGravatar)
.CacheOutput("NoLock");
限制
下列 OutputCacheOptions 屬性可讓您設定適用于所有端點的限制:
- SizeLimit - 快取儲存體的大小上限。 達到此限制時,在收回較舊的專案之前,不會快取任何新的回應。 預設值為 100 MB。
- MaximumBodySize - 如果回應本文超過此限制,則不會快取。 預設值為 64 MB。
- DefaultExpirationTimeSpan - 原則未指定時套用的到期時間持續時間。 預設值為 60 秒。
快取儲存體
IOutputCacheStore 用於儲存體。 根據預設,它會與 MemoryCache 搭配使用。 快取的回應會儲存在程序中,因此每部伺服器都有個別的快取,每當伺服器程序重新啟動時就會遺失。
Redis 快取
替代方法是使用 Redis 快取。 Redis 快取透過存留期超過個別伺服器程序的共用快取,提供伺服器節點之間的一致性。 若要使用 Redis 進行輸出快取:
安裝 Microsoft.AspNetCore.OutputCaching.StackExchangeRedis NuGet 套件。
呼叫
builder.Services.AddStackExchangeRedisOutputCache
(非AddStackExchangeRedisCache
),並且提供指向 Redis 伺服器的連接字串。例如:
builder.Services.AddStackExchangeRedisOutputCache(options => { options.Configuration = builder.Configuration.GetConnectionString("MyRedisConStr"); options.InstanceName = "SampleInstance"; }); builder.Services.AddOutputCache(options => { options.AddBasePolicy(builder => builder.Expire(TimeSpan.FromSeconds(10))); });
options.Configuration
- 指向內部部署 Redis 伺服器或指向裝載供應項目 (例如 Azure Cache for Redis) 的連接字串。 例如,適用於 Azure Cache for Redis 的<instance_name>.redis.cache.windows.net:6380,password=<password>,ssl=True,abortConnect=False
。options.InstanceName
- 選擇性,指定快取的邏輯資料分割。
設定選項與 Redis 型分散式快取選項相同。
不建議使用 IDistributedCache
不建議 IDistributedCache 搭配輸出快取使用。 IDistributedCache
沒有標記所需的不可部分完成功能。 建議您使用 Redis 的內建支援,或藉由在基礎儲存機制上使用直接相依性,建立自訂 IOutputCacheStore 實作。
另請參閱
本文說明如何在 ASP.NET Core 應用程式中設定輸出快取中介軟體。 如需輸出快取的簡介,請參閱輸出快取。
輸出快取中介軟體可用於所有類型的 ASP.NET 核心應用程式:最小 API、具有控制器、MVC 和 Razor Pages 的 Web API。 範例應用程式是基本 API,但它所說明的每個快取功能在其他應用程式類型中也受到支援。
將中介軟體新增至應用程式
呼叫 AddOutputCache,將輸出快取中介軟體新增至服務集合。
呼叫 UseOutputCache,將中介軟體新增至要求處理管線。
注意
設定一個端點或頁面
針對最小 API 應用程式,請設定端點透過呼叫 CacheOutput
來執行快取,或套用 [OutputCache]
屬性,如下列範例所示:
app.MapGet("/cached", Gravatar.WriteGravatar).CacheOutput();
app.MapGet("/attribute", [OutputCache] (context) =>
Gravatar.WriteGravatar(context));
對於具有控制器的應用程式,請將 [OutputCache]
屬性套用至動作方法。 針對 Razor Pages 應用程式,將屬性套用至 Razor 頁面類別。
設定多個端點或頁面
呼叫 AddOutputCache
時建立原則,以指定套用至多個端點的快取組態。 您可以針對特定端點選取原則,而基底原則則提供端點集合的預設快取設定。
下列醒目提示的程式碼會設定所有應用程式端點的快取功能,到期時間為 10 秒。 如果未指定到期時間,則預設值為一分鐘。
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder =>
builder.Expire(TimeSpan.FromSeconds(10)));
options.AddPolicy("Expire20", builder =>
builder.Expire(TimeSpan.FromSeconds(20)));
options.AddPolicy("Expire30", builder =>
builder.Expire(TimeSpan.FromSeconds(30)));
});
下列醒目提示的程式碼會建立兩個原則,每個原則都會指定不同的到期時間。 選取的端點可以使用 20 秒的到期時間,而其他端點則可使用 30 秒的到期時間。
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder =>
builder.Expire(TimeSpan.FromSeconds(10)));
options.AddPolicy("Expire20", builder =>
builder.Expire(TimeSpan.FromSeconds(20)));
options.AddPolicy("Expire30", builder =>
builder.Expire(TimeSpan.FromSeconds(30)));
});
呼叫 CacheOutput
方法或使用 [OutputCache]
屬性時,您可以選取端點的原則:
app.MapGet("/20", Gravatar.WriteGravatar).CacheOutput("Expire20");
app.MapGet("/30", [OutputCache(PolicyName = "Expire30")] (context) =>
Gravatar.WriteGravatar(context));
對於具有控制器的應用程式,請將 [OutputCache]
屬性套用至動作方法。 針對 Razor Pages 應用程式,將屬性套用至 Razor 頁面類別。
預設輸出快取原則
根據預設,輸出快取會遵循下列規則:
- 只會快取 HTTP 200 回應。
- 只會快取 HTTP GET 或 HEAD 要求。
- 設定 cookie 的回應不會被快取。
- 已驗證要求的回應不會被快取。
下列程式碼會將所有預設快取規則套用至應用程式的所有端點:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder.Cache());
});
覆寫預設原則
下列程式碼示範如何覆寫預設規則。 下列自訂原則程式碼中反白顯示的幾行會啟用 HTTP POST 方法和 HTTP 301 回應的快取:
using Microsoft.AspNetCore.OutputCaching;
using Microsoft.Extensions.Primitives;
namespace OCMinimal;
public sealed class MyCustomPolicy : IOutputCachePolicy
{
public static readonly MyCustomPolicy Instance = new();
private MyCustomPolicy()
{
}
ValueTask IOutputCachePolicy.CacheRequestAsync(
OutputCacheContext context,
CancellationToken cancellationToken)
{
var attemptOutputCaching = AttemptOutputCaching(context);
context.EnableOutputCaching = true;
context.AllowCacheLookup = attemptOutputCaching;
context.AllowCacheStorage = attemptOutputCaching;
context.AllowLocking = true;
// Vary by any query by default
context.CacheVaryByRules.QueryKeys = "*";
return ValueTask.CompletedTask;
}
ValueTask IOutputCachePolicy.ServeFromCacheAsync
(OutputCacheContext context, CancellationToken cancellationToken)
{
return ValueTask.CompletedTask;
}
ValueTask IOutputCachePolicy.ServeResponseAsync
(OutputCacheContext context, CancellationToken cancellationToken)
{
var response = context.HttpContext.Response;
// Verify existence of cookie headers
if (!StringValues.IsNullOrEmpty(response.Headers.SetCookie))
{
context.AllowCacheStorage = false;
return ValueTask.CompletedTask;
}
// Check response code
if (response.StatusCode != StatusCodes.Status200OK &&
response.StatusCode != StatusCodes.Status301MovedPermanently)
{
context.AllowCacheStorage = false;
return ValueTask.CompletedTask;
}
return ValueTask.CompletedTask;
}
private static bool AttemptOutputCaching(OutputCacheContext context)
{
// Check if the current request fulfills the requirements
// to be cached
var request = context.HttpContext.Request;
// Verify the method
if (!HttpMethods.IsGet(request.Method) &&
!HttpMethods.IsHead(request.Method) &&
!HttpMethods.IsPost(request.Method))
{
return false;
}
// Verify existence of authorization headers
if (!StringValues.IsNullOrEmpty(request.Headers.Authorization) ||
request.HttpContext.User?.Identity?.IsAuthenticated == true)
{
return false;
}
return true;
}
}
若要使用此自訂原則,請建立具名原則:
builder.Services.AddOutputCache(options =>
{
options.AddPolicy("CachePost", MyCustomPolicy.Instance);
});
然後選取端點的具名原則:
app.MapPost("/cachedpost", Gravatar.WriteGravatar)
.CacheOutput("CachePost");
替代的預設原則覆寫
或者,使用相依性插入 (DI) 初始化實例,並對自訂原則類別進行下列變更:
- 公共建構函式,而不是私有建構函式。
- 排除自訂原則類別中的
Instance
屬性。
例如:
public sealed class MyCustomPolicy2 : IOutputCachePolicy
{
public MyCustomPolicy2()
{
}
類別的其餘部分與先前所示相同。 新增自訂原則,如下列範例所示:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder =>
builder.AddPolicy<MyCustomPolicy2>(), true);
});
上述程式碼會使用 DI 來建立自訂原則類別的實例。 建構函式中的任何公共引數都已解析。
使用自訂原則作為基底原則時,請勿在任何基底原則應套用的端點上呼叫 OutputCache()
(不含引數)。 呼叫 OutputCache()
會將預設原則新增至端點。
指定快取索引鍵
根據預設,URL 的每個部分都會包含為快取專案的索引鍵,也就是配置、主機、連接埠、路徑和查詢字串。 不過,您可能想要明確控制快取索引鍵。 例如,假設您有一個端點,其只會針對 culture
查詢字串的每個唯一值傳回唯一回應。 URL 的其他部分變化,例如其他查詢字串,不應該產生不同的快取專案。 您可以在原則中指定這類規則,如下列醒目提示的程式碼所示:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder
.With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
.Tag("tag-blog"));
options.AddBasePolicy(builder => builder.Tag("tag-all"));
options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
options.AddPolicy("NoCache", builder => builder.NoCache());
options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});
然後,您可以選取端點的 VaryByQuery
原則:
app.MapGet("/query", Gravatar.WriteGravatar).CacheOutput("Query");
以下是控制快取索引鍵的一些選項:
SetVaryByQuery - 指定要新增至快取索引鍵的一個或多個查詢字串名稱。
SetVaryByHeader - 指定要新增至快取索引鍵的一個或多個 HTTP 標頭。
VaryByValue - 指定要新增至快取索引鍵的值。 下列範例會使用值,指出目前的伺服器時間 (秒數) 是否為奇數或偶數。 只有在秒數從奇數到偶數,或從偶數到奇數時,才會產生新的回應。
app.MapGet("/varybyvalue", Gravatar.WriteGravatar) .CacheOutput(c => c.VaryByValue((context) => new KeyValuePair<string, string>( "time", (DateTime.Now.Second % 2) .ToString(CultureInfo.InvariantCulture))));
使用 OutputCacheOptions.UseCaseSensitivePaths 來指定索引鍵的路徑部分會區分大小寫。 預設值是不區分大小寫。
如需其他選項,請參閱 OutputCachePolicyBuilder 類別。
快取重新驗證
快取重新驗證表示伺服器可以傳回 304 Not Modified
HTTP 狀態碼,而不是完整的回應主體。 此狀態碼會通知用戶端,對要求的回應與先前收到的用戶端沒有變更。
下列程式碼說明如何使用 Etag
標頭來啟用快取重新驗證。 如果用戶端傳送具有先前回應之 etag 值的 If-None-Match
標頭,且快取專案是全新的,則伺服器會傳回 304 未修改,而不是完整回應:
app.MapGet("/etag", async (context) =>
{
var etag = $"\"{Guid.NewGuid():n}\"";
context.Response.Headers.ETag = etag;
await Gravatar.WriteGravatar(context);
}).CacheOutput();
執行快取重新驗證的另一種方式是檢查快取專案建立的日期,與用戶端所要求的日期相比較。 當提供要求標頭 If-Modified-Since
時,如果快取的專案較舊且未過期,輸出快取會傳回 304。
快取重新驗證是自動的,以回應從用戶端傳送的這些標頭。 除了啟用輸出快取之外,伺服器上不需要任何特殊設定才能啟用此行為。
使用標記收回快取專案
您可以使用標記來識別端點群組,並收回群組的所有快取專案。 例如,下列程式碼會建立一組端點,其 URL 開頭為「blog」,並將它們標記為「tag-blog」:
app.MapGet("/blog", Gravatar.WriteGravatar)
.CacheOutput(builder => builder.Tag("tag-blog")); ;
app.MapGet("/blog/post/{id}", Gravatar.WriteGravatar)
.CacheOutput(builder => builder.Tag("tag-blog")); ;
指派相同端點組標記的替代方式,是定義套用至開頭為 blog
之端點的基底原則:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder
.With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
.Tag("tag-blog"));
options.AddBasePolicy(builder => builder.Tag("tag-all"));
options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
options.AddPolicy("NoCache", builder => builder.NoCache());
options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});
另一個替代方法是呼叫 MapGroup
:
var blog = app.MapGroup("blog")
.CacheOutput(builder => builder.Tag("tag-blog"));
blog.MapGet("/", Gravatar.WriteGravatar);
blog.MapGet("/post/{id}", Gravatar.WriteGravatar);
在上述標籤指派範例中,這兩個端點都是由 tag-blog
標記來識別。 然後,您可以使用參考該標記的單一陳述式來收回這些端點的快取專案:
app.MapPost("/purge/{tag}", async (IOutputCacheStore cache, string tag) =>
{
await cache.EvictByTagAsync(tag, default);
});
使用此程式碼,傳送至 https://localhost:<port>/purge/tag-blog
的 HTTP POST 要求將會收回這些端點的快取專案。
您可能想要一種方式來收回所有端點的所有快取專案。 若要這樣做,請建立所有端點的基底原則,如下列程式碼所示:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder
.With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
.Tag("tag-blog"));
options.AddBasePolicy(builder => builder.Tag("tag-all"));
options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
options.AddPolicy("NoCache", builder => builder.NoCache());
options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});
此基底原則可讓您使用「全部標記」標籤來收回快取中的所有專案。
停用資源鎖定
根據預設,會啟用資源鎖定,以降低快取踩踏和驚群效應的風險。 如需詳細資訊,請參閱輸出快取。
若要停用資源鎖定,請在建立原則時呼叫 SetLocking(false),如下列範例所示:
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder
.With(c => c.HttpContext.Request.Path.StartsWithSegments("/blog"))
.Tag("tag-blog"));
options.AddBasePolicy(builder => builder.Tag("tag-all"));
options.AddPolicy("Query", builder => builder.SetVaryByQuery("culture"));
options.AddPolicy("NoCache", builder => builder.NoCache());
options.AddPolicy("NoLock", builder => builder.SetLocking(false));
});
下列範例會選取端點的無鎖定原則:
app.MapGet("/nolock", Gravatar.WriteGravatar)
.CacheOutput("NoLock");
限制
下列 OutputCacheOptions 屬性可讓您設定適用于所有端點的限制:
- SizeLimit - 快取儲存體的大小上限。 達到此限制時,在收回較舊的專案之前,不會快取任何新的回應。 預設值為 100 MB。
- MaximumBodySize - 如果回應本文超過此限制,則不會快取。 預設值為 64 MB。
- DefaultExpirationTimeSpan - 原則未指定時套用的到期時間持續時間。 預設值為 60 秒。
快取儲存體
IOutputCacheStore 用於儲存體。 根據預設,它會與 MemoryCache 搭配使用。 不建議 IDistributedCache 搭配輸出快取使用。 IDistributedCache
沒有標記所需的不可部分完成功能。 建議您使用直接相依性來建立自訂 IOutputCacheStore 實作,例如 Redis。 或使用 .NET 8 中 Redis 快取的內建支援。
另請參閱
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應