迁移至.NET 6后Web API控制器无法获取原始请求体的问题
.NET 6获取原始请求体为空的解决方法
问题原因
在.NET 6中,当控制器方法使用[FromBody]参数时,模型绑定系统会先读取Request.Body的内容,且默认情况下该流不可重读(CanSeek为false),流的位置会停在末尾,此时直接读取会得到空字符串。而.NET Framework的Request.Content流默认支持Seek操作,因此旧代码可以通过重置流位置读取内容。
解决方向
方法1:在控制器内启用缓冲并重置流位置
在读取原始请求体前,先启用请求体缓冲并重置流的起始位置,读完后再次重置以保证后续操作正常:
public async Task<IActionResult> PostAsync([FromBody] FooModel model) { string rawContent = string.Empty; // 启用请求体缓冲,允许流被重读 Request.EnableBuffering(); // 将流位置重置到开头 Request.Body.Seek(0, System.IO.SeekOrigin.Begin); using (var reader = new StreamReader(Request.Body, encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: false, leaveOpen: true)) { rawContent = await reader.ReadToEndAsync(); } // 再次重置流位置,避免后续依赖请求体的操作失效 Request.Body.Seek(0, System.IO.SeekOrigin.Begin); // 后续业务代码 return Ok(); }
方法2:使用中间件提前捕获原始请求体
更推荐的方式是通过中间件在模型绑定前读取原始请求体,并存入HttpContext.Items供控制器使用,避免流读取冲突:
- 编写请求日志中间件:
public class RequestBodyCaptureMiddleware { private readonly RequestDelegate _next; public RequestBodyCaptureMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { // 启用缓冲,允许流被多次读取 context.Request.EnableBuffering(); // 读取原始请求体,leaveOpen设为true避免关闭流 using var reader = new StreamReader(context.Request.Body, Encoding.UTF8, false, leaveOpen: true); var rawRequestBody = await reader.ReadToEndAsync(); // 重置流位置,确保模型绑定等后续操作能正常读取 context.Request.Body.Seek(0, System.IO.SeekOrigin.Begin); // 将原始请求体存入HttpContext.Items,供控制器获取 context.Items["RawRequestBody"] = rawRequestBody; // 继续执行后续中间件 await _next(context); } }
- 在
Program.cs中注册中间件(需放在路由之前):
var builder = WebApplication.CreateBuilder(args); // 添加服务... var app = builder.Build(); // 注册请求体捕获中间件 app.UseMiddleware<RequestBodyCaptureMiddleware>(); // 其他中间件配置(如路由、授权等) app.UseRouting(); app.UseAuthorization(); app.MapControllers(); app.Run();
- 在控制器中获取原始请求体:
public async Task<IActionResult> PostAsync([FromBody] FooModel model) { string rawContent = string.Empty; if (HttpContext.Items.TryGetValue("RawRequestBody", out var rawBodyObj) && rawBodyObj is string rawBody) { rawContent = rawBody; } // 后续业务代码 return Ok(); }
注意事项
EnableBuffering()会将请求体缓存到内存(小请求)或磁盘(大请求),需根据业务场景评估性能影响。- 使用
StreamReader时,设置leaveOpen: true可以避免读取后关闭流,确保后续操作能正常使用请求体。
内容的提问来源于stack exchange,提问作者ste22
相关产品推荐
相关产品推荐

