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
相关产品推荐
相关产品推荐

