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

.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 04:53:13