.NET 5中将响应缓存保存至服务器的实现方案咨询
.NET Core/.NET 5 服务器端响应缓存(OutputCache)实现方案
.NET Core 3.x / .NET 5 确实没有内置和 .NET Framework MVC 完全一致的服务器端 OutputCache 功能,你可以通过自定义过滤器 + 缓存组件的方式实现,用法可以做到和旧版 OutputCache 高度接近,步骤如下:
1. 基础依赖配置
首先引入缓存相关组件,单实例场景用内存缓存即可,分布式场景可以替换为 Redis 等分布式缓存:
- 安装 NuGet 包:
Microsoft.Extensions.Caching.Memory - 在 Startup.cs 中注册服务:
// 注册内存缓存服务 services.AddMemoryCache(); // 注册自定义缓存过滤器 services.AddScoped<ServerOutputCacheAttribute>();
2. 自定义缓存特性实现
直接继承 ActionFilterAttribute,模拟旧版 OutputCache 的常用参数:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class ServerOutputCacheAttribute : ActionFilterAttribute { // 缓存时长,单位:秒 public int Duration { get; set; } = 60; // 按请求参数区分缓存,多个参数用逗号分隔,填*代表匹配所有参数 public string VaryByParam { get; set; } = "*"; // 按请求头区分缓存,多个头用逗号分隔 public string VaryByHeader { get; set; } = ""; private const string CacheKeyPrefix = "ServerOutputCache_"; public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var cache = context.HttpContext.RequestServices.GetRequiredService<IMemoryCache>(); var cacheKey = BuildCacheKey(context.HttpContext); // 缓存命中直接返回结果 if (cache.TryGetValue(cacheKey, out IActionResult cachedResult)) { context.Result = cachedResult; return; } // 缓存未命中,执行接口逻辑并缓存结果 var executedContext = await next(); if (executedContext.Result != null && context.ModelState.IsValid) { var cacheOptions = new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(Duration) }; cache.Set(cacheKey, executedContext.Result, cacheOptions); } } // 生成唯一缓存Key private string BuildCacheKey(HttpContext context) { var keyParts = new List<string> { CacheKeyPrefix, context.Request.Path }; // 拼接参数维度 if (!string.IsNullOrEmpty(VaryByParam)) { if (VaryByParam == "*") { keyParts.Add(context.Request.QueryString.ToString()); } else { var paramNames = VaryByParam.Split(',', StringSplitOptions.RemoveEmptyEntries); foreach (var param in paramNames) { var paramVal = context.Request.Query[param.Trim()].ToString(); keyParts.Add($"{param.Trim()}={paramVal}"); } } } // 拼接请求头维度 if (!string.IsNullOrEmpty(VaryByHeader)) { var headerNames = VaryByHeader.Split(',', StringSplitOptions.RemoveEmptyEntries); foreach (var header in headerNames) { var headerVal = context.Request.Headers[header.Trim()].ToString(); keyParts.Add($"{header.Trim()}={headerVal}"); } } return string.Join("_", keyParts); } }
3. 使用方法和 .NET MVC 完全一致
直接在 Controller 或者 Action 上标记特性即可:
// 缓存60秒,按所有请求参数区分缓存 [ServerOutputCache(Duration = 60, VaryByParam = "*")] public IActionResult List(int page, string keyword) { return View(); } // 缓存300秒,仅按id参数区分缓存 [ServerOutputCache(Duration = 300, VaryByParam = "id")] public IActionResult Detail(int id) { return View(); }
补充说明
- 如果需要缓存视图渲染后的 HTML 而非 ActionResult,可以将逻辑迁移到
ResultFilter中,将最终响应流内容作为缓存值存储即可 - 分布式场景下把
IMemoryCache替换为IDistributedCache,缓存内容做序列化处理即可适配 - 后续如果升级到 .NET 7+ 版本,可以直接使用框架内置的
[OutputCache]特性,原生支持服务器端缓存,用法和 .NET MVC 版本基本一致
内容的提问来源于stack exchange,提问作者Blackerback
相关产品推荐
相关产品推荐

