C# Minimal API:输出缓存
发布: (2025年12月20日 GMT+8 01:56)
4 min read
原文: Dev.to
Source: Dev.to
在服务器上存储生成的响应,并直接提供,而无需重新执行端点。
Microsoft Docs
输出缓存是一种 作为中间件实现的服务器端缓存机制。它会存储完整的 HTTP 响应,并在后续请求中直接返回,而无需再次执行端点。
- 默认使用 内存存储,但也可以使用 Redis 等分布式存储作为后端。
- 它最适用于在定义的时间窗口内返回相同响应的 高开销服务器端操作。
工作原理
当请求到达时:
- 缓存命中 – 已存在缓存的响应 → 中间件短路管道并返回缓存的响应。
- 缓存未命中 – 没有缓存的响应 → 正常处理请求,并将响应缓存以供后续请求使用。
添加输出缓存
- 使用
AddOutputCache()注册服务。 - 定义一个或多个策略。
- 将策略应用到端点。
- 使用
UseOutputCache()添加中间件。
var builder = WebApplication.CreateBuilder(args);
// 1️⃣ Register output‑cache services
builder.Services.AddOutputCache(options =>
{
// Default policy (10 s)
options.AddBasePolicy(policy => policy.Expire(TimeSpan.FromSeconds(10)));
// Named policy (20 s)
options.AddPolicy("OutputCache20Seconds", policy => policy.Expire(TimeSpan.FromSeconds(20)));
});
var app = builder.Build();
// 2️⃣ Apply policies to Minimal API endpoints
app.MapGet("/default-cache-policy", () => new[] { "someresponse2" })
.CacheOutput(); // uses default policy
app.MapGet("/custom-cache-policy", () => new[] { "someresponse" })
.CacheOutput("OutputCache20Seconds"); // uses named policy
// 3️⃣ Add the middleware (must be after UseCors() if you use CORS)
app.UseOutputCache();
app.Run();
注意: 在使用 CORS 中间件的应用程序中,
UseOutputCache()必须在UseCors()之后 调用。
带 Authorization 头的输出缓存
默认情况下,当请求中包含 Authorization 头时,输出缓存 不会缓存响应——这是为了避免将已认证的内容错误地提供给其他用户的安全措施。
要覆盖此行为,请创建自定义的输出缓存策略:
internal static class CustomOutputCachingPolicyFactory
{
internal static CustomOutputCachingPolicy Create(TimeSpan expiration)
=> new(expiration);
}
internal sealed class CustomOutputCachingPolicy : IOutputCachePolicy
{
private readonly TimeSpan _expiration;
internal CustomOutputCachingPolicy(TimeSpan expiration) => _expiration = expiration;
public ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellation)
{
var canCache = HttpMethods.IsGet(context.HttpContext.Request.Method) ||
HttpMethods.IsHead(context.HttpContext.Request.Method);
context.EnableOutputCaching = canCache;
context.AllowCacheLookup = canCache;
context.AllowCacheStorage = canCache;
context.AllowLocking = true;
context.ResponseExpirationTimeSpan = _expiration;
context.CacheVaryByRules.QueryKeys = "*";
return ValueTask.CompletedTask;
}
public ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellation)
=> ValueTask.CompletedTask;
public ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellation)
{
var response = context.HttpContext.Response;
// Do not cache if Set‑Cookie header is present
if (!StringValues.IsNullOrEmpty(response.Headers.SetCookie))
{
context.AllowCacheStorage = false;
return ValueTask.CompletedTask;
}
// Only cache successful (200) or permanent redirect (301) responses
if (response.StatusCode is not (StatusCodes.Status200OK or StatusCodes.Status301MovedPermanently))
{
context.AllowCacheStorage = false;
}
return ValueTask.CompletedTask;
}
}
简便扩展(适用于 Minimal API)
这些扩展简化了自定义策略的注册:
public static class OutputCachingExtensions
{
public static void AddCustomOutputCachingPolicy(
this OutputCacheOptions options,
params (string Name, TimeSpan Expiration)[] policies)
{
foreach (var (name, expiration) in policies)
{
options.AddPolicy(name,
CustomOutputCachingPolicyFactory.Create(expiration));
}
}
public static IServiceCollection AddOutputCacheWithCustomPolicy(
this IServiceCollection services,
params (string Name, TimeSpan Expiration)[] policies) =>
services.AddOutputCache(opts => opts.AddCustomOutputCachingPolicy(policies));
public static IServiceCollection AddOutputCacheWithCustomPolicy(
this IServiceCollection services,
Action<OutputCacheOptions> configure,
params (string Name, TimeSpan Expiration)[] policies) =>
services.AddOutputCache(opts =>
{
configure(opts);
opts.AddCustomOutputCachingPolicy(policies);
});
}
附加帮助方法:
options.AddCustomOutputCachingPolicy(policies);
configureOptions.Invoke(options);
});
public static RouteHandlerBuilder CustomCacheOutput(this RouteHandlerBuilder routeHandlerBuilder, string name)
=> routeHandlerBuilder.CacheOutput(name);
重要提示:除非响应对所有用户完全相同,否则不要在需要身份验证或针对特定用户的端点上使用输出缓存。
何时使用
当响应 生成成本高 且不经常更改时使用输出缓存:
- 昂贵的服务器端计算
- 高执行成本的频繁请求响应(例如,报告生成、用户资料渲染)