[OutputCache]不生效是因为未调用builder.Services.AddOutputCache()注册服务,或app.UseOutputCache()顺序错误(须在UseRouting之后、MapControllers之前),导致缓存中间件未介入请求流。

加了 [OutputCache] 却没缓存?不是代码写错了,是服务根本没注册或中间件顺序错了。
为什么 [OutputCache] 特性完全不生效
ASP.NET Core 7+ 的输出缓存是独立中间件,和旧版 [ResponseCache] 没关系,也不共享任何逻辑。它不会自动启用——必须显式注册服务 + 启用中间件,缺一不可。
-
builder.Services.AddOutputCache()必须调用,否则IOutputCacheStore解析失败,所有缓存操作静默跳过 -
app.UseOutputCache()必须放在app.UseRouting()之后、app.MapControllers()或app.MapGet()之前;顺序错,中间件根本不介入请求流 - 即使你写了
[OutputCache(Duration = 60)],上面两步任一缺失,响应头里也不会出现X-Output-Cache: Hit,日志里也看不到Cache hit for key
Minimal API 中怎么配缓存策略
Minimal API 不支持特性,得用扩展方法,但容易写错类型或漏注册策略。
- 最简写法:
app.MapGet("/time", () => DateTime.UtcNow).CacheOutput()—— 使用默认策略(20 分钟,按路径 + 查询参数 +Accept头生成键) - 指定命名策略:
app.MapGet("/api/items", GetItems).CacheOutput("paged"),前提是已在AddOutputCache()里注册:options.AddPolicy("paged", p => p.Expire(TimeSpan.FromMinutes(5)).VaryByQueryKeys("page", "size")) - 别用
.WithMetadata(new OutputCacheAttribute { Duration = 60 }):它不触发策略注册,且在 Minimal API 中实际被忽略;推荐直接写.CacheOutput(p => p.Duration(60))
VaryByQueryKeys 写错的后果很直接
这个参数决定哪些查询参数参与缓存键计算。写错不是“缓存失效”,而是“缓存错乱”或“缓存爆炸”。
- 写成
VaryByQueryKeys = new[] { "*" }或设VaryByAll = true:每个唯一查询字符串都生成新缓存项,比如带时间戳的分页请求,缓存项数会指数级增长,相当于没缓存 - 漏掉关键参数:比如分页接口只写
"page"却漏了"size",那么/items?page=1&size=10和/items?page=1&size=20会共用一个缓存,返回错误数据 - 注意:带查询参数的请求默认就按全部参数区分,所以不写
VaryByQueryKeys时,?id=1和?id=2天然就是不同缓存项
验证是否真命中缓存,别只看响应头
很多人只检查 Cache-Control 响应头,但那只是策略声明,不代表实际命中。真正有效的判断依据只有两个:
- 响应头中出现
X-Output-Cache: Hit(注意是Hit,不是Miss或没有) - 日志里输出类似
Cache hit for key 'OutputCache-xxx'的记录(需开启日志级别为Debug或Information) - 如果用了自定义缓存键(如
VaryByHeader("Authorization")),但后端逻辑其实没做对应处理,会导致缓存碎片化——键变多,命中率暴跌,而你可能根本没意识到
最容易被忽略的是中间件顺序和策略注册的耦合关系:哪怕只改一行 UseOutputCache() 的位置,整个缓存链就断了,而且没有任何报错提示。


















