.Net核心中间件httpContext不支持并发读写
Concurrent reads or writes are not supported.
writing is not allowed after writer was completed
看到早上的日出,今天又是充满希望的一天,开开心心上班到中午,系统出现一个神奇的问题。国内度神上狂搜,结果哪,没什么结果....搬个梯子去google看看,可能梯子午睡还没醒,10个网页9个打不开.。也许真的是充满希望的一天,在google上只打开了两个网页,其中一个就是解决方案。
环境:net core 5.0 web api + ES
我在.Net核心中添加了一个中间件,该中间件记录了每个api请求和对nLog的响应。它对我来说很好用,即使没有发现任何问题。今天,我试图向API发出批量请求,但收到了异常,这是个很异常很有意思,第一次请求接口可正常访问,第二次出现异常“Concurrent reads or writes are not supported.”,这个错误,只有使用 Framework 程序访问才会出现。
1 { 2 "Timestamp":"2021-03-02T15:21:17.6426051+08:00", 3 "Level":"Error", 4 "MessageTemplate":"Unexpected exception in "{ClassName}.{MethodName}".", 5 "Exception":"System.InvalidOperationException: Writing is not allowed after writer was completed. 6 at System.IO.Pipelines.ThrowHelper.ThrowInvalidOperationException_NoWritingAllowed() 7 at System.IO.Pipelines.Pipe.GetMemory(Int32 sizeHint) 8 at System.IO.Pipelines.Pipe.DefaultPipeWriter.GetMemory(Int32 sizeHint) 9 at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext.ReadBody()", 10 "Properties": 11 { 12 "ClassName":"IISHttpContext", 13 "MethodName":"ReadBody", 14 "EventId": 15 { 16 "Id":3, 17 "Name":"UnexpectedError" 18 }, 19 "SourceContext":"Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer", 20 "RequestId":"8000bab3-0000-ff00-b63f-84710c7967bb", 21 "RequestPath":"/api/v1/ProductBatch/ProductBatchAdd" 22 } 23 }
换了另一个日志记录,出现另一个错误“writing is not allowed after writer was completed”
1 [18:00:37 ERR] Unexpected exception in "IISHttpContext.ReadBody". 2 System.InvalidOperationException: Writing is not allowed after writer was completed. 3 at System.IO.Pipelines.ThrowHelper.ThrowInvalidOperationException_NoWritingAllowed() 4 at System.IO.Pipelines.Pipe.GetMemory(Int32 sizeHint) 5 at System.IO.Pipelines.Pipe.DefaultPipeWriter.GetMemory(Int32 sizeHint) 6 at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext.ReadBody()
.Net核心中间件httpContext不支持并发读写
我在这个水平上有些困惑
- .Net核心是否为每个请求创建单独的管道。我认为是的,因为HttpContext.Items对于每个请求周期都是唯一的。
- 如果是,那为什么我会收到并发错误。作为我的代码,首先记录请求,然后将上下文传递给下一个中间件,最后记录响应。
请您分享更好的方法来解决此问题。
1 public class LoggingMiddleware 2 { 3 readonly ILogger logger; 4 readonly RequestDelegate next; 5 public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger) 6 { 7 this.logger = logger; 8 this.next = next; 9 } 10 11 public async Task Invoke(HttpContext context) 12 { 13 LogRequest(context); 14 var originalBodyStream = context.Response.Body; 15 using (var responseBody = new MemoryStream()) 16 { 17 context.Response.Body = responseBody; 18 await next(context).ConfigureAwait(false); 19 LogResponse(context); 20 await responseBody.CopyToAsync(originalBodyStream).ConfigureAwait(false); 21 } 22 } 23 private async void LogRequest(HttpContext context) 24 { 25 var request = context.Request; 26 request.EnableRewind(); 27 var buffer = new byte[Convert.ToInt32(request.ContentLength)]; 28 await request.Body.ReadAsync(buffer, 0, buffer.Length); 29 string requestBody = Encoding.UTF8.GetString(buffer); 30 request.Body.Seek(0, SeekOrigin.Begin); 31 context.Items["Request"] = requestBody; //Request is being used in Nlog config 32 logger.LogInformation("Request received ......"); // Some information 33 34 context.Items.Remove("Request"); 35 } 36 37 private async void LogResponse(HttpContext context) 38 { 39 var response = context.Response; 40 response.Body.Seek(0, SeekOrigin.Begin); 41 var responseText = await new StreamReader(response.Body).ReadToEndAsync().ConfigureAwait(false); 42 response.Body.Seek(0, SeekOrigin.Begin); 43 44 context.Items["Response"] = responseText; 45 logger.LogInformation("Returned response for url with status"); // Information 46 context.Items.Remove("Response"); 47 } 48 49 }
主要问题出在“ await request.Body.ReadAsync(buffer, 0, buffer.Length); ” ,需要使用 await 修改为同步模式。
参考
https://stackoverflow.com/questions/58095947/net-core-middleware-httpcontext-concurrent-read-and-write-not-supported