Rest PUT Web API中的模型属性绑定不是wokring-ASP.NET Core 3.1

Rest PUT Web API中的模型属性绑定不是wokring-ASP.NET Core 3.1,rest,asp.net-core,asp.net-core-3.1,Rest,Asp.net Core,Asp.net Core 3.1,我有一个PUT Rest API,我想从主体和路由参数进行绑定 代码 模型类是 public class StatusRequest { [FromRoute(Name = "Id")] [Required(ErrorMessage = "'Id' attribute is required.")] public string Id { get; set; } [FromBody] [Required(Error

我有一个PUT Rest API,我想从主体和路由参数进行绑定

代码

模型类是

public class StatusRequest 
{
    [FromRoute(Name = "Id")]
    [Required(ErrorMessage = "'Id' attribute is required.")]
    public string Id { get; set; }

    [FromBody]
    [Required(ErrorMessage = "'Status' attribute is required.")]
    public string Status { get; set; }
}

当我向这个API发出请求时,即使我显式地添加了
FromRoute
属性,Id也没有映射到模型。有什么建议吗?

模型绑定将有效覆盖模型类中的[FromRoute]选项。这是故意的(为什么,我不确定,但这是微软的决定)。请参阅此文档的“[FromBody]属性”部分:。正如上面所指出的:“当[FromBody]应用于复杂类型参数时,任何应用于其属性的绑定源属性都会被忽略。”因此,在模型中添加“[FromRoute]”属性没有任何作用……它会被忽略。您可以从模型中删除这两个属性

因此,解决方法是将路由绑定作为方法参数放入put操作中,然后在使用模型之前将其手动添加到控制器中的模型中

[HttpPut("{Id}/someStuffApi")]
public ActionResult UpdateStatus(int Id, [FromBody] StatusRequest StatusRequest)
{
    StatusRequest.Id = Id;

    // remaining code...
}
此方法的缺点是所需属性不能保留在Id参数上。在模型绑定时它将为空,如果.Net Core 3.1自动模型验证处于活动状态,那么它将始终返回422。因此,如果您必须在将其添加到模型之前手动检查

[HttpPut("{Id}/someStuffApi")]
public ActionResult UpdateStatus(int Id, [FromBody] StatusRequest StatusRequest)
{
    StatusRequest.Id = Id;

    // remaining code...
}

如果您想要更大的灵活性,可以查看类似HybridModelBinding NuGet包的东西,该包允许使用属性进行各种模型绑定组合。但这是一个第三方依赖,您可能不希望。()

您可以使用自定义模型绑定,下面是一个演示:

TestModelBinderProvider:

public class TestModelBinderProvider : IModelBinderProvider
    {
        private readonly IList<IInputFormatter> formatters;
        private readonly IHttpRequestStreamReaderFactory readerFactory;

        public TestModelBinderProvider(IList<IInputFormatter> formatters, IHttpRequestStreamReaderFactory readerFactory)
        {
            this.formatters = formatters;
            this.readerFactory = readerFactory;
        }

        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if (context.Metadata.ModelType == typeof(StatusRequest))
                return new StatusBinder(formatters, readerFactory);

            return null;
        }
    }
行动:

[HttpPut("{Id}/someStuffApi")]
        public ActionResult UpdateStatus(StatusRequest StatusRequest)
        {
            return Ok();
        }
结果:

这完全是我所知道的,我认为有办法实现这一点,我将看看HybridModelBinding:(我想微软没有考虑到他们一次验证了更多的参数(想象一下,从标题、路线、正文中分为20个部分)使用例如IValidator要困难得多,这需要付出努力,但您可以通过HybridModelBinding库轻松实现这一点
public class StatusBinder: IModelBinder
    {
        private BodyModelBinder defaultBinder;

        public StatusBinder(IList<IInputFormatter> formatters, IHttpRequestStreamReaderFactory readerFactory)
        {
            defaultBinder = new BodyModelBinder(formatters, readerFactory);
        }

        public async Task BindModelAsync(ModelBindingContext bindingContext)
        {
            // callinng the default body binder
            await defaultBinder.BindModelAsync(bindingContext);
            if (bindingContext.Result.IsModelSet)
            {
                var data = bindingContext.Result.Model as StatusRequest;
                if (data != null)
                {
                    var value = bindingContext.ValueProvider.GetValue("Id").FirstValue;
                    data.Id = value.ToString();


                    bindingContext.Result = ModelBindingResult.Success(data);
                }

            }

        }
    }
    [Required(ErrorMessage = "'Status' attribute is required.")]
    public string Status { get; set; }
}
[HttpPut("{Id}/someStuffApi")]
        public ActionResult UpdateStatus(StatusRequest StatusRequest)
        {
            return Ok();
        }