You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 22:45:29