My API controller has an endpoint that looks like this:
public async Task<IActionResult> Update([FromBody] UpdateCommand command) { /* ... */ }
That command
looks like this:
public class UpdateCommand {
public string Username { get; set; }
public Id Id { get; set; } // <--- here's the problem
}
That Id
is a value object that sorta looks like this:
public class Id : SimpleValueObject<long> {
// base class has: IComparable, equality, GetHashCode, etc.
public Id(long value) : base(value) { }
public Id(string value) : base(Parse(value)) { }
private static long Parse(string value) { /* ... */ }
}
The client would send this:
{
"username": "foo",
"id": 1
}
Now I want model binding to automagically work. But I'm confused how to do that.
I implemented a IModelBinder
and IModelBinderProvider
, but that didn't help. Then I noticed the docs say this:
Typically shouldn't be used to convert a string into a custom type, a TypeConverter is usually a better option.
So I implemented a TypeConverter
, and that also didn't help.
Then I thought to implement a JsonConverter<T>
, but the framework now uses something other than Newtonsoft, so I didn't get far.
So my question is: what must I do to facilitate automatic binding for my custom type. I only need to know which path to pursue, I'll figure out the rest.
(As a side issue: please help me understand when I should implement a model binder vs type converter vs json converter.)
I still don't understand when to use a custom model binder vs custom type converter vs custom json converter.
But it seems like the solution for this scenario is a custom JsonConverter<T>
.
This works for me:
public class IdConverter : JsonConverter<Id> {
public override Id? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
if (reader.TokenType is not JsonTokenType.String and not JsonTokenType.Number)
throw new JsonException();
try {
if (reader.TokenType is JsonTokenType.String) {
var value = reader.GetString();
return new Id(value);
}
else {
var value = reader.GetInt64();
return new Id(value);
}
}
catch {
throw new JsonException();
}
}
public override void Write(Utf8JsonWriter writer, Id value, JsonSerializerOptions options) =>
writer.WriteNumberValue(value.Value);
}