ASP.NET WebApi 2:禁用包含 ISO8601 日期的字符串参数的反序列化

ASP.NET WebApi 2: Disable deserialization of string argument containing ISO8601 Date

我有一个 Asp.Net WebApi 控制器,具有以下操作:

public void Post([FromBody]object value)

现在,这个 value 参数有时作为包含 ISO8601 格式日期的 String 发送,有时作为 DateTime 发送。数据以 JSON 格式发送。

对于这两个选项中的每一个,我都必须做不同的事情,所以我需要做出区分,但我总是以 DateTime 值结束。

我知道我的源 DateTime 值在发送到上述操作时被序列化为 JSON 作为 ISO8601 格式的字符串(我正在使用 HttpClient 用于实际发送),因此我的操作无法区分两者。

我的问题是是否可以在发送的值中包含某种类型提示,以便告诉我的 WebApi 端点正在发送哪种特定类型(并在反序列化 value 上强制执行该类型).

最终通过实现自定义 JsonConverter 解决了这个问题,该自定义 JsonConverter 序列化字符串值和类型提示(类似于 Newtonsoft.JsonJsonSerializerSettings 中的 MetadataPropertyHandling 设置) .

下面是实现技巧的实现:

public class DateStringTypeConservingConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsAssignableFrom(typeof(string));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.ReadFrom(reader);
        var typeMetadata = token["$type"];

        if (typeMetadata?.Value<string>() == typeof(string).FullName)
        {
            return token.Value<string>("$value");
        }

        // Default behavior
        return serializer.Deserialize(reader, objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value is string)
        {
            WriteJsonStringWithType(writer, value as string);
        }
        else
        {
            // Default behavior
            serializer.Serialize(writer, value);
        }
    }

    // Write the given string to the given JSON writer with type info:
    // { $type: "System.String", $value: "<string content>" }
    private void WriteJsonStringWithType(JsonWriter writer, string value)
    {
        writer.WriteStartObject();

        writer.WritePropertyName("$type");
        writer.WriteValue(typeof(string).FullName);

        writer.WritePropertyName("$value");
        writer.WriteValue(value);

        writer.WriteEndObject();
    }
}

还有一点用法示例:

    static void Main(string[] args)
    {
        var dateString = new Wrapper
        {
            Value = "2017-01-08T21:24:48.114Z"
        };

        var converters = new JsonConverter[] { new DateStringTypeConservingConverter() };
        var serializedDateString = JsonConvert.SerializeObject(dateString, new JsonSerializerSettings
        {
            Converters = converters
        });

        var deserializedDateString = 
            JsonConvert.DeserializeObject<Wrapper>(serializedDateString, converters);

        // Output: System.String
        Console.WriteLine($"The type of deserialized value is: { deserializedDateString.Value.GetType() }");

        Console.ReadKey();
    }

    class Wrapper
    {
        public object Value { get; set; }
    }