.NET API自定义ICar模型绑定:BindingContext无值原因问询
问题原因
当你在参数上使用[FromBody]时,ASP.NET Core会通过InputFormatter(默认是JsonInputFormatter)来解析请求体中的JSON内容,而非通过ModelBinder的ValueProvider获取数据。ValueProvider默认仅处理查询字符串、表单数据等请求数据,不会包含请求体的JSON内容,所以你在CarModelBinder中通过bindingContext.ValueProvider.GetValue("type")自然拿不到值。
解决方案
针对你的场景,有两种标准实现方式:
方式一:修改ModelBinder,手动读取请求体
移除[FromBody]属性,在绑定器中直接读取并解析请求体的JSON:
public class CarModelBinder : IModelBinder { public async Task BindModelAsync(ModelBindingContext bindingContext) { ArgumentNullException.ThrowIfNull(bindingContext); // 读取请求体JSON using var reader = new StreamReader(bindingContext.HttpContext.Request.Body); var json = await reader.ReadToEndAsync(); if (string.IsNullOrEmpty(json)) { bindingContext.Result = ModelBindingResult.Failed(); return; } var carElement = JsonSerializer.Deserialize<JsonElement>(json); if (!carElement.TryGetProperty("type", out var typeElement)) { bindingContext.Result = ModelBindingResult.Failed(); return; } // 解析CarType枚举 if (!Enum.TryParse<CarType>(typeElement.ToString(), out var carType)) { bindingContext.Result = ModelBindingResult.Failed(); return; } // 根据类型反序列化为对应实现类 ICar car = carType switch { CarType.Fun => JsonSerializer.Deserialize<FunCar>(json), CarType.Sport => JsonSerializer.Deserialize<SportCar>(json), _ => throw new NotSupportedException("不支持的车辆类型") }; bindingContext.Result = ModelBindingResult.Success(car); } }
修改端点定义,移除[FromBody]:
[HttpPost("AddCar")] public ActionResult<ICar> AddCar([ModelBinder(typeof(CarModelBinder))] ICar car) { Console.WriteLine(car.Type); return car; }
方式二:实现IInputFormatter(推荐,符合ASP.NET Core设计)
InputFormatter是处理请求体解析的标准组件,更适合JSON场景:
public class CarInputFormatter : TextInputFormatter { public CarInputFormatter() { SupportedMediaTypes.Add(MediaTypeNames.Application.Json); SupportedEncodings.Add(Encoding.UTF8); SupportedEncodings.Add(Encoding.Unicode); } protected override bool CanReadType(Type type) { return typeof(ICar).IsAssignableFrom(type); } public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding) { using var reader = new StreamReader(context.HttpContext.Request.Body, encoding); var json = await reader.ReadToEndAsync(); var carElement = JsonSerializer.Deserialize<JsonElement>(json); if (!carElement.TryGetProperty("type", out var typeElement)) { return await InputFormatterResult.FailureAsync(); } if (!Enum.TryParse<CarType>(typeElement.ToString(), out var carType)) { return await InputFormatterResult.FailureAsync(); } ICar car = carType switch { CarType.Fun => JsonSerializer.Deserialize<FunCar>(json), CarType.Sport => JsonSerializer.Deserialize<SportCar>(json), _ => null }; return car == null ? await InputFormatterResult.FailureAsync() : await InputFormatterResult.SuccessAsync(car); } }
在Program.cs中注册这个InputFormatter:
builder.Services.AddControllers(options => { // 插入到默认格式化器之前,确保优先处理ICar类型 options.InputFormatters.Insert(0, new CarInputFormatter()); });
此时端点可以正常使用[FromBody],无需额外绑定器属性:
[HttpPost("AddCar")] public ActionResult<ICar> AddCar([FromBody] ICar car) { Console.WriteLine(car.Type); return car; }
内容的提问来源于stack exchange,提问作者clfaster
相关产品推荐
相关产品推荐

