我想创建和端点符合“JSON合并补丁”https://tools.ietf.org/html/rfc7396
请不要将它与“JavaScript Object Notation(JSON)Patch”https://tools.ietf.org/html/rfc6902混淆
但是,我在区分请求中的两种情况时遇到了一些问题:
{
surname: "Kowalski"
email: null
}
{
surname: "Kowalski"
}
出现问题的原因是在模型绑定后的两种情况下,电子邮件的值都为null。
您是否有建议如何实施?
您需要3种不同的电子邮件状态:
[email protected]
)null
值所以问题实际上是如何在模型的string
属性中表达这三种状态。你不能只使用原始的string
属性来执行此操作,因为null
值和缺失值会因为您正确描述而发生冲突。解决方案是使用一些标志来指示值是否在请求中提供。您可以将此标志作为模型中的另一个属性,或者在string
上创建一个简单的包装器,与Nullable<T>
类非常相似。我建议创建简单的通用OptionalValue<T>
类:
public class OptionalValue<T>
{
private T value;
public T Value
{
get => value;
set
{
HasValue = true;
this.value = value;
}
}
public bool HasValue { get; set; }
}
然后你需要自定义JsonConverter
,可以将通常的json值反序列化为OptionalValue<T>
:
class OptionalValueConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(OptionalValue<T>);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return new OptionalValue<T>
{
Value = (T) reader.Value,
};
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
你的模型看起来像这样:
public class SomeModel
{
public string Surname { get; set; }
[JsonConverter(typeof(OptionalValueConverter<string>))]
public OptionalValue<string> Email { get; set; } = new OptionalValue<string>();
}
请注意,您使用空的OptionalValue<string>()
分配电子邮件。如果输入json不包含email
值而不是Email
属性将保持OptionalValue
与HasValue
设置为false
。如果输入json包含一些email
,甚至null
,那么OptionalValueConverter
将创建OptionalValue
的实例,其中HasValue
设置为true
。
现在在控制器操作中,您可以确定email
的3种状态中的任何一种:
[HttpPatch]
public void Patch([FromBody]SomeModel data)
{
if (data.Email.HasValue)
{
// Email presents in Json
if (data.Email.Value == null)
{
// Email should be removed
}
else
{
// Email should be updated
}
}
else
{
// Email does not present in Json and should not be affected
}
}
当使用不支持undefined
和null
(如JavaScript和TypeScript)之间区别的语言时,这是一个特殊问题。您可以考虑其他选项:
""
删除它,因为空字符串通常不是有效值(也不总是可行)X-MYAPP-SET-EMAIL=true
将删除电子邮件,如果它为null)。缺点是这可能会破坏您的请求和客户开发人员的痛苦上面的每个选项都有其自身的缺点,因此在决定走哪条路之前要仔细考虑。
你能使用JsonMergePatch库吗? https://github.com/Morcatko/Morcatko.AspNetCore.JsonMergePatch
用法很简单:
[HttpPatch]
[Consumes(JsonMergePatchDocument.ContentType)]
public void Patch([FromBody] JsonMergePatchDocument<Model> patch)
{
...
patch.ApplyTo(backendModel);
...
}
它似乎支持将一些属性设置为null,并保持其他属性不变。在内部,JsonMergePatchDocument创建一个JsonPatch文档,其中一个OperationType.Replace用于请求中的每个项目。 https://github.com/Morcatko/Morcatko.AspNetCore.JsonMergePatch/blob/master/src/Morcatko.AspNetCore.JsonMergePatch/Formatters/JsonMergePatchInputFormatter.cs