Custom ModelBinder for Interface .NET API

42 Views Asked by At

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

  • Why is there no value in the BindingContext/ValueProvider?
1

There are 1 best solutions below

1
Qiang Fu On BEST ANSWER

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

Test result
enter image description here
enter image description here