You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

迁移至.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供控制器使用,避免流读取冲突:

  1. 编写请求日志中间件:
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);
    }
}
  1. 在Program.cs中注册中间件(需放在路由之前):
var builder = WebApplication.CreateBuilder(args);

// 添加服务...

var app = builder.Build();

// 注册请求体捕获中间件
app.UseMiddleware<RequestBodyCaptureMiddleware>();

// 其他中间件配置(如路由、授权等)
app.UseRouting();
app.UseAuthorization();

app.MapControllers();

app.Run();
  1. 在控制器中获取原始请求体:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 12:25:42