It’s doable by a custom model binder as mentioned in the comment. Here is a few code snippets to wire everything up, with the example you can send a http request with the following JSON body to an API /api/cats?From=james&Days=20
{
"Name":"",
"EyeColor":"Red"
}
A few classes, you can find them here as well: https://github.com/atwayne/so-51316269
// We read Cat from request body
public class Cat
{
public string Name { get; set; }
public string EyeColor { get; set; }
}
// AdoptionRequest from Query String or Route
public class AdoptionRequest
{
public string From { get; set; }
public string Days { get; set; }
}
// One class to merge them together
[ModelBinder(BinderType = typeof(CatAdoptionEntityBinder))]
public class CatAdoptionRequest
{
public Cat Cat { get; set; }
public AdoptionRequest AdoptionRequest { get; set; }
}
public class CatAdoptionEntityBinder : IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
// Read Cat from Body
var memoryStream = new MemoryStream();
var body = bindingContext.HttpContext.Request.Body;
var reader = new StreamReader(body, Encoding.UTF8);
var text = reader.ReadToEnd();
var cat = JsonConvert.DeserializeObject<Cat>(text);
// Read Adoption Request from query or route
var adoptionRequest = new AdoptionRequest();
var properties = typeof(AdoptionRequest).GetProperties();
foreach (var property in properties)
{
var valueProvider = bindingContext.ValueProvider.GetValue(property.Name);
if (valueProvider != null)
{
property.SetValue(adoptionRequest, valueProvider.FirstValue);
}
}
// Merge
var model = new CatAdoptionRequest()
{
Cat = cat,
AdoptionRequest = adoptionRequest
};
bindingContext.Result = ModelBindingResult.Success(model);
return;
}
}
// Controller
[HttpPost()]
public bool Post([CustomizeValidator]CatAdoptionRequest adoptionRequest)
{
return ModelState.IsValid;
}
public class CatAdoptionRequestValidator : AbstractValidator<CatAdoptionRequest>
{
public CatAdoptionRequestValidator()
{
RuleFor(profile => profile.Cat).NotNull();
RuleFor(profile => profile.AdoptionRequest).NotNull();
RuleFor(profile => profile.Cat.Name).NotEmpty();
}
}
// and in our Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddFluentValidation();
services.AddTransient<IValidator<CatAdoptionRequest>, CatAdoptionRequestValidator>();
}