如何为主体模型上的属性提供自定义模型绑定器?

Eri*_*k T 6 c# model-binding json-deserialization asp.net-core asp.net-core-webapi

我在为 ASP.NET Core 2.1 Web API 服务编写自定义模型绑定程序时遇到问题。我有一个如下的操作方法

public async Task<IActionResult> SaveAccountEvents([FromRoute] Guid accountId, IList<AccountEvent> events)
{
}
Run Code Online (Sandbox Code Playgroud)

AccountEvent 类有很多属性,包括

[BindProperty(BinderType = typeof(CustomBinding))]
public string EventBody { get; set; }
Run Code Online (Sandbox Code Playgroud)

目的是我希望来自客户端的此属性的值是字符串或 JObject (JSON)。这个想法是,客户端可以发送字符串化的 JSON 或 EventBody 中的 JSON,模型会将其存储为字符串 - 最终作为字符串化的 JSON。

我遇到的问题是在我的自定义绑定中,bindingContext.ValueProvider.GetValue永远不会返回任何内容。查看该属性,它只包含一个RouteValueProvider. 我看不出有什么方法可以从身体中获取价值。是否有其他方法可以在 ASP.NET Core Web API 中自定义 JSON 反序列化?

更新:

客户端使用此 API 发送事件数据。事件数据由一个信封组成,其中包含事件名称、发送日期、发起事件的用户等内容。在当前版本中,实际事件数据以字符串化 JSON 形式发送。所以现在,请求看起来像这样(注意事件主体是一个字符串):

{
  "eventDate": "20180909",
  "eventBody": "{
      \"newLastName\": \"bob\",
      \"primaryApplication\": \"bob\"
    }"
}
Run Code Online (Sandbox Code Playgroud)

变化是我希望能够接受 JSON 格式的事件主体。在这种情况下,客户端应该能够发送(请注意,事件正文是正确的 JSON):

{
  "eventDate": "20180909",
  "eventBody": {
    "newLastName": "bob",
    "primaryApplication": "bob"
  }
}
Run Code Online (Sandbox Code Playgroud)

这两种场景都需要支持,现有的API是字符串版本。我想做的是配置 API,以便如果发送的事件是 JSON 对象,则它会在到达 Action 的模型中进行字符串化。

Eri*_*k T 2

感谢柯克指出解决方案。我最终完全跳过了模型绑定(耶!),并且能够使用带有属性上的属性的自定义序列化器来实现我所需要的:[JsonConverter(typeof(EventBodyJsonConverter))]。有了这个,我就可以[FromBody]在操作中使用来获取内容感知绑定。

public class EventBodyJsonConverter : JsonConverter
{
    private JsonSerializer _jsonSerializer;

    public EventBodyJsonConverter()
    {
        _jsonSerializer = new JsonSerializer();
    }

    public override bool CanConvert(Type objectType) => objectType == typeof(string);

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartObject)
        {
            var eventJObject = _jsonSerializer.Deserialize<JObject>(reader);
            return eventJObject.ToString();
        }

        if (reader.TokenType == JsonToken.String)
        {
            return reader.Value.ToString();
        }

        throw new Exception();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {

    }
}
Run Code Online (Sandbox Code Playgroud)