ASP.NET Core 中读取Post Request.Body 的正确姿势
.NET Core 2.* 获取 Post 的 Body 内容字符串
ASP.NET Core 中的 Request.Body 虽然是一个 Stream ,但它是一个与众不同的 Stream —— 不允许 Request.Body.Position=0 ,这就意味着只能读取一次,要想多次读取,需要借助 MemoryStream ,详见博问 asp.net core中2次读取Request.Body的问题
using (var buffer = new MemoryStream()) { Request.Body.CopyTo(buffer); buffer.Position = 0; buffer.CopyTo(writer.BaseStream); Console.WriteLine("Request.Body:"); buffer.Position = 0; buffer.CopyTo(Console.OpenStandardOutput()); }
昨天读了博文 Reading request body in ASP.NET Core 之后得知在 ASP.NET Core 2.0 中已经针对这个问题提供了解决方法 —— EnableRewind() ,只要启用倒带功能,就可以让 Request.Body 回归正常 Stream 。
使用非常简单,引用命名空间 Microsoft.AspNetCore.Http.Internal ,调用方法 Request.EnableRewind() 即可,下面我们用简单的示例代码体验一下
public class HomeController : Controller { public IActionResult Index() { Request.EnableRewind(); // .net core 3.0为 Request.EnableBuffering (); Console.WriteLine("Request.Body1:"); Request.Body.Position = 0; Request.Body.CopyTo(Console.OpenStandardOutput()); Console.WriteLine(); Console.WriteLine("Request.Body2:"); Request.Body.Position = 0; Request.Body.CopyTo(Console.OpenStandardOutput()); Console.WriteLine(); return Ok(); } }
EnableRewind 有 2 个参数 bufferThreshold 与 bufferLimit 。 bufferThreshold 设置的是 Request.Body 最大缓存字节数(默认是30K),超出这个阈值的字节会被写入磁盘;bufferLimit 设置的是 Request.Body 允许的最大字节数(默认值是null),超出这个限制,就会抛出异常 System.IO.IOException 。
EnableRewind 的实现源代码见 BufferingHelper.cs
.NET Core 3.* 获取 Post 的 Body 内容字符串
更新:.NET Core 3.0 中 EnableRewind 被改名为 EnableBuffering 。
[HttpPost] public WXNotifyResponse WXNotify(WXNotifyData request) { Request.EnableBuffering(); Request.Body.Position = 0; string body; using (var reader = new StreamReader(Request.Body)) { body = reader.ReadToEnd(); } return Content(body); }
因为使用了一个 using,所以该方法调用过了之后,Request.Body就被 释放 了,不能再读取
.NET5 获取 Post 的 Body 内容字符串
在 Startup.cs 的 Configure 方法中添加配置
//允许body重用
app.Use(next => new RequestDelegate(
  async context =>
  {
	  context.Request.EnableBuffering();
	  await next(context);
  }
));Post 数据 Body Data 获取方法如下:
[HttpPost] public async Task<string> GetBodyData(WXNotifyData request) { //Request.EnableBuffering(); string body = ""; Request.Body.Position = 0; using (var reader = new StreamReader(Request.Body)) { body = await reader.ReadToEndAsync(); } return body; }
因为使用了一个 using,所以该方法调用过了之后,Request.Body就被 释放 了,不能再读取
.NET获取Post Request Body数据可以新建一个扩展 【推荐使用】
public static class HttpRequestExtensions
{
    /// <summary>
	/// 获得body请求
	/// </summary>
	/// <param name="request"></param>
	/// <returns></returns>
	public static string GetBodyData(this HttpRequest request)
	{
		request.EnableBuffering();
		// 改方法 StreamReader释放的时候,也会把request.Body对象释放掉,然后以后的请求就不能再次读取request.Body了
		/*
		request.Body.Seek(0, SeekOrigin.Begin);
		using (var reader = new StreamReader(request.Body, Encoding.UTF8))
		{
			var param = reader.ReadToEnd();
			return param;
		}
		*/
		// 这里由于没有释放StreamReader 担心内存问题
		/*
		string body = "";
		// 获取请求参数
		long? length = request.HttpContext.Request.ContentLength;
		//request.HttpContext.Request.Body.Position = 0;
		request.Body.Seek(0, SeekOrigin.Begin);
		if (length != null && length > 0)
		{
			// 使用这个方式读取,并且使用异步
			StreamReader streamReader = new StreamReader(request.HttpContext.Request.Body, Encoding.UTF8);
			body = streamReader.ReadToEndAsync().Result;
		}
		return body;
		*/
		string body = "";
		var buffer = new MemoryStream();
		request.Body.Seek(0, SeekOrigin.Begin);
		request.Body.CopyTo(buffer);
		buffer.Position = 0;
		try
		{
			using (StreamReader streamReader = new StreamReader(buffer, Encoding.UTF8))
			{
				body = streamReader.ReadToEndAsync().Result;
			}
		}
		finally
		{
			if (buffer != null)
				buffer.Dispose();
		}
		return body;
	}
}使用
[HttpPost] public async Task<string> GetBodyData(WXNotifyData request) { //Request.EnableBuffering(); string body = Request.GetBodyData(); return body; }
 
 

