ASP.NET Core如何仅在启用响应缓存时按查询字符串区分缓存
实现方案
不要直接在控制器上硬编码带VaryByQueryKeys的[ResponseCache]特性,通过ASP.NET Core的应用模型约定,在启动阶段检测消费端是否注册了响应缓存服务,再动态给类库内的控制器追加对应配置,即可兼容两种运行场景,不会抛出异常。
具体实现步骤
- 编写自定义控制器模型约定,针对类库内的控制器按需加载缓存规则:
public class LibraryControllerResponseCacheConvention : IControllerModelConvention { private readonly bool _isResponseCachingEnabled; public LibraryControllerResponseCacheConvention(bool isResponseCachingEnabled) { _isResponseCachingEnabled = isResponseCachingEnabled; } public void Apply(ControllerModel controller) { // 仅匹配当前类库下的控制器,避免影响消费端的其他控制器 if (controller.ControllerType.Assembly != typeof(ClassLibraryController).Assembly) { return; } var cacheAttribute = new ResponseCacheAttribute { Duration = int.MaxValue, Location = ResponseCacheLocation.Any }; // 仅在消费端启用响应缓存时,才设置VaryByQueryKeys规则 if (_isResponseCachingEnabled) { cacheAttribute.VaryByQueryKeys = new[] { "*" }; } controller.Filters.Add(cacheAttribute); } }
- 给类库添加控制器注册扩展方法,自动完成服务检测和约定注入,消费端不需要编写额外逻辑:
public static class MvcBuilderExtensions { public static IMvcBuilder AddLibraryControllers(this IMvcBuilder builder) { // 检测消费端是否已经调用AddResponseCaching注册了响应缓存服务 bool hasResponseCachingService = builder.Services.Any(s => s.ServiceType == typeof(IResponseCachingPolicyProvider) ); // 加载类库的控制器程序集 builder.AddApplicationPart(typeof(ClassLibraryController).Assembly); // 注入自定义缓存约定 builder.Services.Configure<MvcOptions>(options => { options.Conventions.Add(new LibraryControllerResponseCacheConvention(hasResponseCachingService)); }); return builder; } }
运行效果
- 消费端未启用响应缓存(未调用
AddResponseCaching()、未注册响应缓存中间件):仅给响应添加public缓存头,和原有逻辑完全一致,不会抛出异常 - 消费端启用响应缓存:自动追加
VaryByQueryKeys = new[] {"*"}配置,响应会按查询字符串参数区分缓存,不会出现不同参数返回相同缓存结果的问题
不要尝试在类库中强制注册响应缓存中间件,中间件执行顺序由消费端的请求管道配置决定,强制注入很容易导致中间件顺序异常,破坏消费端的原有业务逻辑。
内容的提问来源于stack exchange,提问作者Ted Nyberg
相关产品推荐
相关产品推荐

