小白细节思考之读取Request.Body
小白最近做的项目当中涉及到了WebApi,具体是三方程序通过调用小白写的 WebApi 进行推送数据,调用的次数、频率是没有限制的,因此就涉及到了并发。
小白使用.NET 8 创建了WebApi,在读取Request请求时,使用如下的方式:
1 var requestContent = string. Empty; 2 using (var stream = new MemoryStream()) 3 using (var reader = new StreamReader(stream)) 4 { 5 await Request.Body.CopyToAsync(stream); 6 stream.Seek(0, SeekOrigin.Begin); 7 requestContent = await reader.ReadToEndAsync(); 8 }
使用这种方式,在Request的Body数据量很大并且两个请求同时发起时,会导致在Request的Body未读取完毕时,便已经释放从而导致程序报错。
小白一开始的解决方法是设置Request Body的最大数量,然而并没有什么效果。后来小白考虑是不是将接口改为同步的方式,以达到处理完一个请求再处理另一个请求的目的,这样是不是就不会有提前释放的错误了。
然而虽然接口是同步的方式,但是外部请求来的时候服务器还是会同时处理多个请求。这就引发了小白对IIS处理并发请求的思考,事实上IIS 的线程池会分配适当数量的线程来并行处理这些请求,因此不管接口是否同步,都会并行处理。
并且同步的方式性能并不是很好,微软也不建议这么操作,当程序流量比较大的时候,很容易导致程序不稳定甚至崩溃。
经过小白一番努力最后发现,多个接口里面都用到了上述的代码去读取Request.Body。所以首先将这段代码封装未一个方法,以达到优化代码的目的。
关键的解决方法如下:
StreamReader stream = new StreamReader(Request.Body); string body = stream.ReadToEndAsync().GetAwaiter().GetResult();
不使用MemoryStream,直接使用StreamReader读取,StreamReader可以自动释放故不使用using了,同时将ReadToEndAsync变为同步方法。虽然小白没有真正弄清楚是因为MemoryStream导致的,还是GetAwaiter().GetResult()的神奇之处(小白认为await 和此方法效果一致),但是上面的方法成功解决了小白程序的bug。
具体的原理,小白希望有热爱编码之士仗义出手指教一下。如果引起不到编码高手的注意,那么小白便在编程领域越来越强之后,再回过头来,对其中原理解释一番。