.NET 7 OutputCache如何根据请求体参数实现缓存差异化?
.NET 7 OutputCache 按请求体参数实现缓存差异化
问题背景
我在已完成的应用中使用.NET 7新增的OutputCache,配置代码如下:
builder.Services.AddOutputCache(x => x.AddPolicy("default", new CustomCachePolicy())); app.UseOutputCache(); internal class CustomCachePolicy : IOutputCachePolicy { public ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellation) { context.AllowCacheLookup = true; context.AllowCacheStorage = true; context.AllowLocking = true; context.EnableOutputCaching = true; context.ResponseExpirationTimeSpan = TimeSpan.FromSeconds(10); return ValueTask.CompletedTask; } public ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellation) => ValueTask.CompletedTask; public ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellation) => ValueTask.CompletedTask; }
需要缓存的API接口:
[HttpPost("Filter")] [OutputCache( PolicyName = "default" )] public ActionResult<GenericResponse<IQueryable<CategoryEntity>>> Filter(CategoryFilterDto dto) => Result(_repository.Filter(dto));
该接口根据请求体参数做筛选,希望缓存能根据请求体参数实现差异化,但.NET 7中原有VaryByParams属性已不存在,求实现方案。
实现方案
在.NET 7的OutputCache体系中,要基于请求体参数区分缓存,需在自定义IOutputCachePolicy中手动扩展缓存键的生成逻辑,将请求体内容纳入缓存键的判断依据。具体实现步骤如下:
1. 扩展自定义缓存策略,添加请求体到缓存键
修改CustomCachePolicy的CacheRequestAsync方法,读取请求体内容并追加到缓存键构建器中:
internal class CustomCachePolicy : IOutputCachePolicy { public async ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellation) { context.AllowCacheLookup = true; context.AllowCacheStorage = true; context.AllowLocking = true; context.EnableOutputCaching = true; context.ResponseExpirationTimeSpan = TimeSpan.FromSeconds(10); // 仅针对POST请求处理请求体 if (context.HttpContext.Request.Method.Equals(HttpMethods.Post, StringComparison.OrdinalIgnoreCase)) { // 启用请求体缓冲,确保流可多次读取 context.HttpContext.Request.EnableBuffering(); // 读取请求体内容 using var reader = new StreamReader(context.HttpContext.Request.Body, leaveOpen: true); var requestBody = await reader.ReadToEndAsync(cancellation); // 重置流指针,保证后续中间件能正常读取请求体 context.HttpContext.Request.Body.Position = 0; // 将请求体内容添加到缓存键,实现不同请求体对应不同缓存项 context.CacheKeyBuilder.Append(requestBody); } } public ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellation) => ValueTask.CompletedTask; public ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellation) => ValueTask.CompletedTask; }
2. 关键注意事项
- 请求体可复用性:必须调用
EnableBuffering(),因为默认情况下POST请求的流是不可重读的,后续模型绑定等操作还需要读取请求体,读取完成后要重置流指针到起始位置。 - 性能优化:如果请求体体积较大,直接将完整内容加入缓存键会影响存储效率和性能,可对请求体做哈希处理,用哈希值代替原内容:
using var sha256 = SHA256.Create(); var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(requestBody)); var hashString = Convert.ToBase64String(hashBytes); context.CacheKeyBuilder.Append(hashString); - 请求方法过滤:只对需要处理请求体的HTTP方法(如POST、PUT)执行上述逻辑,避免对GET等无请求体的请求做无效操作。
内容的提问来源于stack exchange,提问作者SinaMN75
相关产品推荐
相关产品推荐

