在ASP.NET Core中实现“JSON Merge Patch” - 最好的方法是区分null和未定义的属性
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在ASP.NET Core中实现“JSON Merge Patch” - 最好的方法是区分null和未定义的属性相关的知识,希望对你有一定的参考价值。
我想创建和端点符合“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种不同的电子邮件状态:
- 更新的填充值(例如
test@mail.com
) - 如果要删除电子邮件,
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)之间区别的语言时,这是一个特殊问题。您可以考虑其他选项:
- 使用PUT(并不总是可行的)
- 对于字符串使用
""
删除它,因为空字符串通常不是有效值(也不总是可行) - 添加一个额外的自定义标头,以指示您是否确实要删除该值,并将默认值设置为false(例如,
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
以上是关于在ASP.NET Core中实现“JSON Merge Patch” - 最好的方法是区分null和未定义的属性的主要内容,如果未能解决你的问题,请参考以下文章
在 ASP.NET Core 2.1 Web API 中实现分页