路由数据和请求主体的复杂类型参数绑定

问题描述

在我的Web Api控制器中,我执行以下操作

public Data GetData(ComplexModel model)

模型是

class ComplexModel
{
     int Id { get; set;}
     string Name { get; set;}
     string AddressLine { get; set;}
}

我可以绑定来自路由参数的Id属性和来自正文的NameAddressLine吗?

以下代码无效

class ComplexModel
{
     [Fromroute]
     int Id { get; set;}
     [FromBody]
     string Name { get; set;}
     [FromBody]
     string AddressLine { get; set;}
}

解决方法

这是一个演示:

ComplexModel:

public class ComplexModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string AddressLine { get; set; }
    }

控制器:

[Route("api/[controller]")]
    [ApiController]
    public class ApiController : ControllerBase
    {
        [Route("/Index/{Id}")]
        public IActionResult Index(ComplexModel complexModel)
        {
            return Ok();
        }
    }

CustomBinder:

public class CustomBinder:IModelBinder
    {
        private BodyModelBinder defaultBinder;

        public CustomBinder(IList<IInputFormatter> formatters,IHttpRequestStreamReaderFactory readerFactory) // : base(formatters,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 ComplexModel;
                if (data != null)
                {
                    var value = bindingContext.ValueProvider.GetValue("Id").FirstValue;
                    int intValue = 0;
                    if (int.TryParse(value,out intValue))
                    {
                        // Override the Id property
                        data.Id = intValue;
                    }
                    

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

            }

        }

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(ComplexModel))
                return new CustomBinder(formatters,readerFactory);

            return null;
        }
    }
            
        }

startup.cs:

 services.AddMvc()
  .AddMvcOptions(options =>
  {
      IHttpRequestStreamReaderFactory readerFactory = services.BuildServiceProvider().GetRequiredService<IHttpRequestStreamReaderFactory>();
      options.ModelBinderProviders.Insert(0,new TestModelBinderProvider(options.InputFormatters,readerFactory));
  });

结果: enter image description here