asp.net core 7+ output caching 必须显式调用addoutputcache()注册服务和useoutputcaching()插入中间件,且后者须位于userouting()之后、mapcontrollers()之前;仅加[outputcache]特性无效。

ASP.NET Core 7+ 的 Output Caching 不是“加个特性就生效”,必须显式注册服务和中间件,漏掉任意一步,缓存完全不工作,且无任何错误提示。
Program.cs 里必须写的两行代码
Output Caching 是基于中间件的机制,不是装饰器式魔法。它不会因为你写了 [OutputCache] 就自动启动。
-
builder.Services.AddOutputCache()—— 注册缓存服务(默认用内存缓存,支持替换为分布式缓存) -
app.UseOutputCache()—— 插入中间件链,位置必须在app.UseRouting()之后、app.MapControllers()或app.MapEndpoints()之前
常见错误:把 UseOutputCache() 放在 UseRouting() 前 → 中间件没收到路由上下文,直接跳过;放在 MapControllers() 后 → 请求已处理完毕,缓存逻辑根本没机会执行。
[OutputCache] 特性 vs [ResponseCache]:别混用
[ResponseCache] 只写 HTTP 缓存头(如 Cache-Control: public, max-age=60),不走 Output Caching 的内存/分布式缓存后端,也不支持键变换、策略复用等能力。
- 用
[OutputCache(Duration = 60)]:真正缓存响应体,命中时返回X-Output-Cache: Hit,支持VaryByQueryKeys、VaryByHeader等精细控制 - 用
[ResponseCache]:仅客户端或代理缓存,服务端每次仍要执行 Action,适合纯前端缓存场景 - 同时存在时,
[ResponseCache]会被忽略(Output Caching 中间件接管了整个响应生命周期)
Minimal API 中对应的是 .WithMetadata(new OutputCacheAttribute { Duration = 60 }) 或更推荐的 .CacheOutput("MyPolicy")。
缓存键怎么算?默认行为容易踩坑
Output Caching 默认按以下维度生成缓存键:HTTP 方法 + 路径 + 查询字符串 + Accept + Accept-Encoding + Accept-Language(取决于实际请求头)。
-
/api/items?id=1和/api/items?id=2→ 两个独立缓存项 - 带
utm_source=google这类跟踪参数 → 每个不同值都生成新缓存,迅速打爆内存 - 想忽略某些查询参数,得用策略:例如
options.AddPolicy("ByItemId", b => b.Expire(TimeSpan.FromMinutes(10)).VaryByQuery("id")) - 绝对避免
VaryByQuery("*")或VaryByAll,等于关闭缓存
调试时打开日志:"Microsoft.AspNetCore.OutputCaching": "Information",看日志里是否出现 Cache hit for key 'xxx',比只看响应头更可靠。
缓存没生效?先查这三件事
缓存静默失败是常态,因为框架不报错也不警告。
- 确认
AddOutputCache()和UseOutputCache()都写了,且顺序正确 - 确认没残留
UseResponseCaching()(旧版中间件),它与 Output Caching 冲突,会导致策略被跳过 - 确认 Action 返回的是可缓存的响应:GET/HEAD 请求、状态码 200/204/301/302/404,且响应体未被标记为不可缓存(如设置了
Cache-Control: no-store)
最隐蔽的问题是:你改了代码、重启了应用,但缓存项还在内存里,旧响应仍在返回——这时候需要清空缓存或改策略名强制刷新,而不是怀疑配置错了。











