sree
sree

Reputation: 2367

Complex type parameter binding from route data and request body

In my Web Api controller I have the below action

public Data GetData(ComplexModel model)

Model is

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

Can I bind Id property from a route parameter and Name & AddressLine from body?

Following code doesn't work

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

Upvotes: 0

Views: 1252

Answers (1)

Yiyi You
Yiyi You

Reputation: 18159

Here is a demo:

ComplexModel:

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

Controller:

[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));
  });

result: enter image description here

Upvotes: 1

Related Questions