ASP.NET Core后台服务读取Request.Body流出现ObjectDisposedException异常
问题原因
- ASP.NET Core 中
Request.Body流的生命周期与当前 HTTP 请求上下文完全绑定,当 Controller 的Post方法执行完成返回Accepted响应后,框架会自动销毁整个请求上下文,关联的Request.Body流也会被同步释放。 - 后台服务为异步队列消费,消费时机晚于接口响应返回,此时流已经被释放,读取操作自然会触发
System.ObjectDisposedException异常。
解决方案
核心逻辑为:不要直接将 Request.Body 流存入队列,在请求未结束的 Controller 执行阶段,提前将流中内容完整读取后,把读取结果存入队列即可。
步骤1:修改队列存储类型
将BackgroundJobs中队列的存储类型从Stream调整为实际的内容类型(字符串/字节数组,根据业务需求选择即可):
public class BackgroundJobs { // 示例为存储序列化后的Body字符串,也可按需替换为byte[] public ConcurrentQueue<string> Queue { get; set; } = new ConcurrentQueue<string>(); }
步骤2:调整Controller入队逻辑
在Post方法中提前读取Request.Body的内容后再入队,注意将方法改为异步签名:
[HttpPost] public async Task<ActionResult<string>> Post([FromQuery] string validationToken = null) { if (!string.IsNullOrEmpty(validationToken)) { return Ok(validationToken); } else { // 读取请求Body内容 using var streamReader = new StreamReader(Request.Body); string bodyContent = await streamReader.ReadToEndAsync(); backgroundJobs.Queue.Enqueue(bodyContent); return Accepted(); } }
如果你的业务中后续还有其他逻辑需要读取Request.Body,请在读取前调用Request.EnableBuffering(),读取完成后将Request.Body.Position重置为0即可。
步骤3:调整后台服务消费逻辑
直接从队列中获取预读取的内容处理即可,不需要再读流:
if (this.backgroundJobs.Queue.TryDequeue(out string content)) { var list = JsonConvert.DeserializeObject<Notifications>(content); // 后续业务逻辑 }
如果请求Body体积普遍较大,为避免内存占用过高,也可选择将Body写入临时文件,将临时文件路径存入队列,消费完成后删除文件即可。
内容的提问来源于stack exchange,提问作者ChsharpNewbie
相关产品推荐
相关产品推荐

