.NET5下Json自定义ContractResolver的ShouldSerialize谓词内获取请求查询字符串问题
问题根因
你遇到的ObjectDisposedException本质不是IHttpContextAccessor注入错误,而是你在CreateProperties方法执行时就提前捕获了当时的Request对象存入闭包:CreateProperties仅会在对应类型首次序列化时执行一次,闭包中存储的是第一次请求的Request实例,后续该实例被回收释放,再次调用就会抛出已释放异常。
低开销解决方案
不需要每次请求重建合约,仅需调整两处逻辑即可,整体额外开销几乎可以忽略:
1. 调整ShouldSerialize的上下文获取时机
将HttpContext的访问逻辑推迟到ShouldSerialize委托执行时触发,该委托每次序列化属性时都会执行,此时拿到的永远是当前请求的上下文实例,不会缓存旧对象。
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { var properties = base.CreateProperties(type, memberSerialization); foreach (var property in properties) { // 不要提前捕获HttpContext/Request实例 property.ShouldSerialize = p => { // 委托执行时才取当前请求的上下文 var currentContext = _httpContextAccessor.HttpContext; // 非请求场景(后台任务、单元测试等)下的兜底逻辑 if (currentContext == null) { return true; } var targetQueryValue = currentContext.Request.Query["你的查询参数名"]; // 原有判断逻辑,返回是否序列化该属性 return /* 你的业务判断结果 */; }; } return properties; }
2. 修正注册逻辑,避免手动构建根容器
你原来在配置委托中调用services.BuildServiceProvider()是错误实践,会生成和应用运行容器隔离的冗余容器,改为通过PostConfigure从应用根容器获取服务即可:
public void ConfigureServices(IServiceCollection services) { services.AddHttpContextAccessor(); // 注册自定义合约解析器 services.AddSingleton<MyContractResolver>(); services.AddControllers() .AddNewtonsoftJson(); // 配置NewtonsoftJson选项,从容器获取依赖 services.PostConfigure<MvcNewtonsoftJsonOptions>((sp, opt) => { opt.SerializerSettings.ContractResolver = sp.GetRequiredService<MyContractResolver>(); }); }
方案优势
CreateProperties仍仅会在每个类型首次序列化时执行一次,无重复创建合约的额外开销ShouldSerialize中访问HttpContext是从AsyncLocal存储中读取当前上下文,属于轻量操作,性能损耗极低- 天然兼容后续请求参数变化的场景,每次执行都会读取最新的查询字符串
内容的提问来源于stack exchange,提问作者themanwhojaped
相关产品推荐
相关产品推荐

