.NET7中ActionFilterAttribute获取HttpContext.Request.Body始终为空的问题
在.NET7的ActionFilterAttribute中正确获取请求体并检查属性拼写错误
一、确保请求缓冲中间件配置正确
请求体默认只能被读取一次,必须在路由管道前启用请求缓冲,且中间件顺序不能错。以下是Program.cs的正确配置:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers() .AddJsonOptions(options => { // 可选:根据需求配置大小写敏感性,方便后续属性校验 options.JsonSerializerOptions.PropertyNameCaseInsensitive = false; }); var app = builder.Build(); // 启用请求缓冲,必须放在UseRouting之前 app.Use(async (context, next) => { context.Request.EnableBuffering(); await next(); }); app.UseRouting(); app.UseAuthorization(); app.MapControllers(); app.Run();
二、修改ActionFilterAttribute实现正确读取与校验
核心是读取请求体后重置流位置,保证后续模型绑定正常工作,同时对比模型属性检查拼写错误:
using Microsoft.AspNetCore.Mvc.Filters; using System.Reflection; using System.Text; using System.Text.Json; public class ValidateRequestBodyAttribute : ActionFilterAttribute { public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var request = context.HttpContext.Request; if (request.ContentType?.Contains("application/json") == true && request.Body.CanSeek) { // 重置流到起始位置,避免因其他组件提前读取导致空内容 request.Body.Position = 0; using var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true); var requestBody = await reader.ReadToEndAsync(); // 重置流位置,确保后续模型绑定能正常读取 request.Body.Position = 0; if (!string.IsNullOrEmpty(requestBody)) { await ValidateUndefinedProperties(requestBody, context); } } await next(); } private async Task ValidateUndefinedProperties(string requestBody, ActionExecutingContext context) { // 解析原始JSON,保留所有传入属性 using var jsonDoc = JsonDocument.Parse(requestBody); var rootObj = jsonDoc.RootElement; // 获取ScoreModel的所有属性名称 var validProperties = typeof(ScoreModel).GetProperties(BindingFlags.Public | BindingFlags.Instance) .Select(p => p.Name) .ToHashSet(StringComparer.OrdinalIgnoreCase); // 忽略大小写,可根据需求调整 // 遍历所有传入属性,校验是否存在于模型中 foreach (var prop in rootObj.EnumerateObject()) { if (!validProperties.Contains(prop.Name)) { context.ModelState.AddModelError(prop.Name, $"属性 '{prop.Name}' 不存在或拼写错误"); } } // 若存在错误,直接返回BadRequest,终止后续Action执行 if (!context.ModelState.IsValid) { context.Result = new Microsoft.AspNetCore.Mvc.BadRequestObjectResult(context.ModelState); } } }
三、ScoreModel示例
public class ScoreModel { public int Id { get; set; } public string StudentName { get; set; } public decimal MathScore { get; set; } public decimal EnglishScore { get; set; } }
四、控制器中使用过滤器
[ApiController] [Route("api/scores")] public class ScoresController : ControllerBase { [HttpPost] [ValidateRequestBody] public IActionResult SubmitScore(ScoreModel model) { // 正常业务逻辑处理 return Ok(model); } }
常见问题排查
- 确认缓冲中间件在
UseRouting之前配置,否则缓冲不生效 - 检查请求的
ContentType是否为application/json,非JSON请求需调整过滤器中的判断逻辑 - 确保
request.Body.CanSeek返回true,若为false则说明缓冲未正确启用 - 若有其他中间件(如日志中间件)提前读取了请求体,需确保它们读取后也重置了流位置
内容的提问来源于stack exchange,提问作者MRSeen
相关产品推荐
相关产品推荐

