.NET 6中如何在到达Controller前读取JSON原始Post请求体?
.NET 6 同路由多请求体场景的模型绑定与验证实现
针对同路由HttpPost接口需支持10余种请求体组合、保留原有路由且要自动完成模型验证的需求,可通过自定义模型绑定器+多场景模型类的方案实现,替代Java中用HashMap接收后判断action的方式,同时利用.NET的数据注解完成自动校验。
1. 定义多场景模型类
先创建包含共享字段(如action)的基类,再为每个请求场景创建专属模型,通过数据注解标记必填/校验规则:
using System.ComponentModel.DataAnnotations; // 基类:所有请求共享的字段 public abstract class BaseApprovalRequest { [Required(ErrorMessage = "action字段为必填项")] public string Action { get; set; } } // 示例场景1:审批通过请求 public class ApproveRequest : BaseApprovalRequest { [Required(ErrorMessage = "staffId字段为必填项")] public string StaffId { get; set; } [Range(1, int.MaxValue, ErrorMessage = "approvalLevel必须大于0")] public int ApprovalLevel { get; set; } } // 示例场景2:驳回请求 public class RejectRequest : BaseApprovalRequest { [Required(ErrorMessage = "staffId字段为必填项")] public string StaffId { get; set; } [Required(ErrorMessage = "rejectReason字段为必填项")] public string RejectReason { get; set; } } // 其他场景模型依此类推...
2. 自定义模型绑定器
实现IModelBinder接口,读取原始JSON请求体,根据action字段反序列化为对应场景模型,并处理反序列化错误:
using Microsoft.AspNetCore.Mvc.ModelBinding; using System.Text.Json; public class ApprovalRequestBinder : IModelBinder { public async Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext)); // 读取原始JSON请求体 using var reader = new StreamReader(bindingContext.HttpContext.Request.Body); var jsonBody = await reader.ReadToEndAsync(); if (string.IsNullOrEmpty(jsonBody)) { bindingContext.ModelState.AddModelError(string.Empty, "请求体不能为空"); return; } // 先反序列化为JsonElement获取action字段 var jsonElement = JsonSerializer.Deserialize<JsonElement>(jsonBody); if (!jsonElement.TryGetProperty("action", out var actionElement) || string.IsNullOrEmpty(actionElement.GetString())) { bindingContext.ModelState.AddModelError("action", "action字段不能为空"); return; } string action = actionElement.GetString()!.ToLower(); BaseApprovalRequest requestModel = action switch { "approve" => JsonSerializer.Deserialize<ApproveRequest>(jsonBody), "reject" => JsonSerializer.Deserialize<RejectRequest>(jsonBody), // 添加其他action对应的模型映射 _ => null }; if (requestModel == null) { bindingContext.ModelState.AddModelError("action", $"不支持的action类型:{action}"); return; } // 触发数据注解校验 var validationContext = new ValidationContext(requestModel); var validationResults = new List<ValidationResult>(); if (!Validator.TryValidateObject(requestModel, validationContext, validationResults, validateAllProperties: true)) { foreach (var validationResult in validationResults) { bindingContext.ModelState.AddModelError(validationResult.MemberNames.FirstOrDefault() ?? string.Empty, validationResult.ErrorMessage); } return; } // 绑定成功,设置模型 bindingContext.Result = ModelBindingResult.Success(requestModel); } }
3. 注册模型绑定提供者(可选)
如果不想在每个控制器参数上手动指定绑定器类型,可创建IModelBinderProvider自动匹配基类:
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; public class ApprovalRequestBinderProvider : IModelBinderProvider { public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context.Metadata.ModelType.IsAssignableTo(typeof(BaseApprovalRequest))) { return new BinderTypeModelBinder(typeof(ApprovalRequestBinder)); } return null; } }
在Program.cs中注册:
builder.Services.AddControllers(options => { options.ModelBinderProviders.Insert(0, new ApprovalRequestBinderProvider()); });
4. 控制器实现
直接使用基类作为参数,自动触发模型绑定与验证,无需手动处理原始请求体:
using Microsoft.AspNetCore.Mvc; [ApiController] [Route("your-existing-route")] // 保留原有路由,不可修改 public class ApprovalController : ControllerBase { private readonly IAuthorizationService _authorizationService; private readonly IApprovalService _approvalService; private readonly ILogger<ApprovalController> _logger; public ApprovalController(IAuthorizationService authorizationService, IApprovalService approvalService, ILogger<ApprovalController> logger) { _authorizationService = authorizationService; _approvalService = approvalService; _logger = logger; } [HttpPost] public async Task<IActionResult> PostForApproving([FromBody] BaseApprovalRequest request) { // 自动验证模型,失败直接返回400 if (!ModelState.IsValid) { return BadRequest(ModelState); } // 获取RequestStore(对应Java中的request.getAttribute) var requestStore = HttpContext.Items["requestStore"] as RequestStore; _logger.LogInformation("postForApproving({@RequestStore})", requestStore); // 权限校验 _authorizationService.CheckApiRole(requestStore, $"postForApproving_{entity_name}", "Staff-Management"); // 服务层分发处理(用模式匹配替代Java的else-if) var respBody = request switch { ApproveRequest approveReq => await _approvalService.HandleApprove(requestStore, approveReq), RejectRequest rejectReq => await _approvalService.HandleReject(requestStore, rejectReq), // 其他场景模型的处理分支 _ => throw new NotSupportedException("不支持的请求类型") }; // 构造响应(对应Java的ResponseEntity) var responseHeaders = GetResponseHeaders(requestStore); foreach (var header in responseHeaders) { Response.Headers.Append(header.Key, header.Value); } return Created("", respBody); } // 模拟Java的HttpUtilities.getResponseHeaders方法 private Dictionary<string, string> GetResponseHeaders(RequestStore requestStore) { return new Dictionary<string, string> { // 填充响应头逻辑 }; } }
5. 服务层处理
利用C#的模式匹配,直接根据模型类型分发到对应处理方法:
public interface IApprovalService { Task<Dictionary<string, object>> HandleApprove(RequestStore requestStore, ApproveRequest request); Task<Dictionary<string, object>> HandleReject(RequestStore requestStore, RejectRequest rejectReq); // 其他场景的处理方法 } public class ApprovalService : IApprovalService { // 实现各场景的具体业务逻辑... }
内容的提问来源于stack exchange,提问作者Shameel Perayil
相关产品推荐
相关产品推荐

