I have a WarehouseDTO class with a Name field of a custom type called CustomString. I'm trying to create a custom ModelBinder to bind the type when it is passed from the front end via the controller. My controller:
public async Task<IActionResult> Create([FromBody] WarehouseDTO warehouseDTO)
My ModelBinderProvider and BindModelAsync methods:
using Microsoft.AspNetCore.Mvc.ModelBinding;
public class CustomStringModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
if (bindingContext.BindingSource == BindingSource.Body)
{
var fieldName = bindingContext.FieldName;
string val = bindingContext.ValueProvider.GetValue(fieldName).FirstValue;
if (val == null)
{
val = bindingContext.ValueProvider.GetValue("Value").FirstValue;
}
bindingContext.Result = ModelBindingResult.Success(new CustomString(val));
}
return Task.CompletedTask;
}
}
public class CustomStringBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(CustomString))
{
return new CustomStringModelBinder();
}
return null;
}
}
But when I debug the BinderProvider the only ModelType that comes through is the WarehouseDTO. How can I apply the binder to a field inside the warehouseDTO from the request body? I have added the required line to my Startup.cs to inject the binder provider. Here is my WarehouseDTO:
public class WarehouseDTO
{
public int Id { get; set; }
[ModelBinder(typeof(CustomStringModelBinder))]
public CustomString Name { get; set; }
...
}
I'm trying to create a CustomtString type with which I will use dependency injection to determine how to format the strings of this type, it may be uppercase, title case etc.
As mentioned in the answer above, I was missing the piece which deserializes the json. Here is my json converter:
public class CustomStringJsonConverter : JsonConverter<CustomString>
{
public override CustomString Read(ref Utf8JsonReader reader, Type
typeToConvert, JsonSerializerOptions options)
{
var data = JsonSerializer.Deserialize<string>(ref reader, options);
var result = new CustomString(data!);
return result;
}
public override void Write(Utf8JsonWriter writer, CustomString value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.Value);
}
}
And here's the line in Startup.cs that provides the converter via Dependency Injection:
services.AddControllers(
options =>
{
options.ModelBinderProviders.Insert(1, new
OodleStringBinderProvider());
}
).AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new
OodleStringJsonConverter());
});
Since I also use SignalR I also has to provide the converter for my signalR service in Startup.