I'm trying to write a custom model binder for a .NET API Endpoint. I have a Post
Endpoint with one parameter of type ICar
. I would like to call this endpoint with different implementations of ICar
:
Classes and Interface
public interface ICar
{
CarType Type { get; set; }
}
class FunCar : ICar
{
public CarType Type { get; set; } = CarType.Fun;
public string Color { get; set; } = "red";
}
class SportCar : ICar
{
public CarType Type { get; set; } = CarType.Sport;
}
public enum CarType
{
Fun,
Sport
}
I would like to Convert the passed Object to the correct type with a ModelBinder CarModelBinder
.
public class CarModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
ArgumentNullException.ThrowIfNull(bindingContext);
var valueProviderResult = bindingContext.ValueProvider.GetValue("type");
if (valueProviderResult == ValueProviderResult.None)
{
return Task.CompletedTask;
}
// ... do the matching - never reaching that point
return Task.CompletedTask;
}
}
My Endpoint looks like this:
[HttpPost("AddCar")]
public ActionResult<ICar> AddCar([FromBody] [ModelBinder(typeof(CarModelBinder))] ICar car)
{
Console.WriteLine(car.Type);
return new FunCar();
}
Example Request Body
{
"type": 1
}
Question
You could try following code which read body from httpcontext and use newtonsoft to parse the "type" before de-serialize.
CarModelBinder.cs
public class CarModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
string jsonString;
using (var reader = new StreamReader(bindingContext.HttpContext.Request.Body, Encoding.UTF8))
{
jsonString = reader.ReadToEndAsync().GetAwaiter().GetResult();
}
var jsonObject = (JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(jsonString);
int enumNumber = (int)jsonObject["type"];
if (enumNumber == 0)
{
var model = JsonConvert.DeserializeObject<FunCar>(jsonString);
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
if (enumNumber == 1)
{
var model = JsonConvert.DeserializeObject<SportCar>(jsonString);
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
else
{
return Task.CompletedTask;
}
}
}