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

ASP.NET Core中获取请求体模型外message字段的问题

ASP.NET Core中保留[FromBody]同时读取未绑定字段的替代方案

问题场景

在ASP.NET Core项目中,TicketController的Create方法通过[FromBody]特性绑定Ticket模型,但需要读取请求体中模型未定义的message字段时无法获取内容。移除[FromBody]后能正常读取请求体,但希望保留该特性实现模型绑定,目前已通过全局添加EnableBuffering中间件解决,现寻求其他实现方法。

相关基础代码如下:

控制器代码

public class TicketController : ReadWriteController<TicketView, Ticket>
{
    public override ReadWriteBusiness<TicketView, Ticket> Business => new TicketBusiness();

    protected override ReadBusiness<TicketView> ReadBusiness => new TicketBusiness();

    public override IActionResult Create([FromBody] Ticket model)
    {
        var message = HttpContext.GetBody().Result;

        return OkJson("ok");
    }    
}

扩展方法代码

using Microsoft.AspNetCore.Http;
using System.IO;
using System.Threading.Tasks;

namespace PaydarCore.Api;

public static class ExtensionMethods
{
    public static async Task<string> GetBody(this HttpContext httpContext)
    {
        httpContext.Request.EnableBuffering();
        httpContext.Request.Body.Position = 0;
        var rawRequestBody = await new StreamReader(httpContext.Request.Body).ReadToEndAsync();
        return rawRequestBody;
    }
}

Ticket模型代码

public class Ticket : Entity
{
    public Ticket()
    {
        RelatedItems = new ExpandoObject();
    }

    public override long Id { get; set; }

    public int TicketNumber { get; set; }

    public string Subject { get; set; }

    public long? TicketDepartmentId { get; set; }

    public long TicketPriorityId { get; set; }

    public long TicketStateId { get; set; }
}

请求体内容

{
    "ticketNumber" : 12,
    "subject" :"",
    "ticketPriorityId":1,
    "ticketStateId": 1,
    "message":"fjhsdkfhjsdfhkjsdhf"
}

替代实现方法

方法1:使用ActionFilter提前启用请求缓冲

不需要全局中间件,创建自定义ActionFilter在模型绑定前启用请求体缓冲,确保流可被多次读取:

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Http;

public class EnableRequestBodyBufferingAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        context.HttpContext.Request.EnableBuffering();
        base.OnActionExecuting(context);
    }
}

然后在控制器方法上添加该特性:

[EnableRequestBodyBuffering]
public override async Task<IActionResult> Create([FromBody] Ticket model)
{
    var rawBody = await HttpContext.GetBody();
    // 解析原始请求体获取message字段
    using var doc = JsonDocument.Parse(rawBody);
    var message = doc.RootElement.GetProperty("message").GetString();
    
    return OkJson("ok");
}

方法2:扩展Ticket模型接收额外字段

如果允许修改模型,可直接添加message字段,或用[JsonExtensionData]捕获所有未定义字段:

using System.Text.Json.Serialization;

public class Ticket : Entity
{
    public Ticket()
    {
        RelatedItems = new ExpandoObject();
    }

    public override long Id { get; set; }

    public int TicketNumber { get; set; }

    public string Subject { get; set; }

    public long? TicketDepartmentId { get; set; }

    public long TicketPriorityId { get; set; }

    public long TicketStateId { get; set; }

    // 直接添加对应字段
    public string Message { get; set; }

    // 或用该特性捕获所有未定义字段
    [JsonExtensionData]
    public IDictionary<string, JsonElement> ExtraFields { get; set; }
}

使用JsonExtensionData时,直接从字典读取未定义字段:

public override IActionResult Create([FromBody] Ticket model)
{
    var message = model.ExtraFields["message"].GetString();
    return OkJson("ok");
}

方法3:自定义模型绑定器

创建自定义模型绑定器,同时完成模型绑定和原始请求体的读取,避免重复操作流:

using Microsoft.AspNetCore.Mvc.ModelBinding;
using System.Text.Json;
using System.Threading.Tasks;

public class TicketWithRawBodyModelBinder : IModelBinder
{
    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var httpContext = bindingContext.HttpContext;
        httpContext.Request.EnableBuffering();
        
        // 读取原始请求体
        using var reader = new StreamReader(httpContext.Request.Body);
        var rawBody = await reader.ReadToEndAsync();
        httpContext.Request.Body.Position = 0;
        
        // 绑定Ticket模型
        var ticket = JsonSerializer.Deserialize<Ticket>(rawBody);
        
        // 将解析后的message存入HttpContext供后续使用
        using var doc = JsonDocument.Parse(rawBody);
        httpContext.Items["Message"] = doc.RootElement.GetProperty("message").GetString();
        
        bindingContext.Result = ModelBindingResult.Success(ticket);
    }
}

在模型上配置绑定器:

[ModelBinder(BinderType = typeof(TicketWithRawBodyModelBinder))]
public class Ticket : Entity
{
    // 原有字段不变
}

控制器方法中直接读取:

public override IActionResult Create([FromBody] Ticket model)
{
    var message = HttpContext.Items["Message"] as string;
    return OkJson("ok");
}

内容的提问来源于stack exchange,提问作者Mohammad Miras

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 03:01:06