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

.NET Core 3.1 Web API如何绑定路由参数与请求体字段到同一模型

解决方案

方案1:拆分参数手动赋值(最推荐,无侵入,适配多场景)

该方案改动最小,完全不需要修改UpdateUserCommand模型代码,不影响模型其他使用场景:

[HttpPut("{id}")]
[ProducesResponseType(typeof(UpdateUserCommandResponse), StatusCodes.Status200OK)]
public async Task<ActionResult<UpdateUserCommandResponse>> PutUser(
    // 单独接收路由中的id参数
    int id, 
    // 明确指定模型从Json请求体绑定
    [FromBody] UpdateUserCommand updateUserCommand)
{
    // 手动将路由id赋值给模型
    updateUserCommand.Id = id;
    var response = await _mediator.Send(updateUserCommand);
    return Ok(response);
}

UpdateUserCommand保持无特性的干净状态即可:

public class UpdateUserCommand : IRequest<UpdateUserCommandResponse>
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }        
}

方案2:自定义模型绑定器(适合批量同类型需求)

如果有大量同规则的接口需要实现「路由取Id+Body取其他字段」的绑定逻辑,可以自定义模型绑定器避免重复手动赋值:

  1. 自定义绑定器实现
public class RouteIdAndBodyBinder : IModelBinder
{
    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));

        // 1. 从路由读取Id
        var routeIdResult = bindingContext.ValueProvider.GetValue("id");
        if (routeIdResult == ValueProviderResult.None || !int.TryParse(routeIdResult.FirstValue, out var id))
        {
            bindingContext.ModelState.TryAddModelError("id", "路由参数Id非法");
            bindingContext.Result = ModelBindingResult.Failed();
            return;
        }

        // 2. 从请求体读取剩余字段
        bindingContext.HttpContext.Request.EnableBuffering();
        using var reader = new StreamReader(bindingContext.HttpContext.Request.Body, leaveOpen: true);
        var bodyJson = await reader.ReadToEndAsync();
        bindingContext.HttpContext.Request.Body.Position = 0;
        
        var model = JsonSerializer.Deserialize(bodyJson, bindingContext.ModelType, 
            new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
        
        // 3. 赋值路由Id到模型
        var idProp = bindingContext.ModelType.GetProperty("Id");
        idProp?.SetValue(model, id);

        bindingContext.Result = ModelBindingResult.Success(model);
    }
}
  1. 控制器参数指定绑定器
[HttpPut("{id}")]
[ProducesResponseType(typeof(UpdateUserCommandResponse), StatusCodes.Status200OK)]
public async Task<ActionResult<UpdateUserCommandResponse>> PutUser(
    [ModelBinder(BinderType = typeof(RouteIdAndBodyBinder))] UpdateUserCommand updateUserCommand)
{
    var response = await _mediator.Send(updateUserCommand);
    return Ok(response);
}

注意事项

你之前调整代码时添加的[FromForm]特性仅适配表单格式的请求,如果你需要接收Json请求体,应该使用[FromBody]特性。默认情况下.NET Core Web API复杂类型参数会优先从请求体读取,不会自动合并路由中的同名字段,因此会出现Id始终为0的问题。

内容的提问来源于stack exchange,提问作者Jay

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 15:36:04