我正在使用ServiceStack框架构建RESTful API。我需要更新的很多资源非常庞大,每个类最多有40个属性,所以我想做部分更新而不是替换整个资源。通常客户端只需要更新40个中的一个或两个属性,所以我想发送一个由少数属性组成的JSON主体。
由于所有属性组合都是可能的,因此按照此处的建议为每个类创建一个“更新”类是不可行的:https://github.com/ServiceStack/ServiceStack/wiki/New-Api#patch-request-example
在Microsoft ASP.NET WebAPI OData包中,有一个Delta类,它接受类的子集并根据此子集(http://www.strathweb.com/2013/01/easy-asp-net-web-api-resource-updates-with-delta/)更新资源。这是我想要的功能,因为我将拥有相当多的类,所以通用方法最好。
基本上,如果我有一个班级
public class MyClass {
public int a { get; set; }
public int b { get; set; }
...
public int z { get; set; }
}
我想使用带有正文的PATCH请求更新MyClass的资源
{"a":42,"c":42}
使用ServiceStack是否有标准或推荐的方法来实现这一目标?
答案 0 :(得分:4)
将DTO中的任何标量值声明为可为空。这将允许您确定在请求中实际发送了哪些字段:
public class MyClass {
public int? a { get; set; }
public int? b { get; set; }
public int? c { get; set; }
// etc.
// object-type properties are already nullable of course
public string MyString { get; set; }
}
现在,如果客户端发送部分请求,请执行以下操作:
{ "a": 1, "b": 0 }
在检查DTO时,您将能够确定实际发送了哪些属性:
myClass.a == 1
myClass.b == 0
myClass.c == null
myClass.MyString == null
etc.
为您的DTO设置PATCH
路线并在您的服务中实施Patch
方法:
public object Patch(MyClass request)
{
var existing = GetMyClassObjectFromDatabase();
existing.PopulateWithNonDefaultValues(request);
SaveToDatabase(existing);
...
}
PopulateWithNonDefaultValues
是关键所在。它会将请求对象中的值复制到数据库实体上,但只会复制不是默认值的属性。因此,如果值为null,则不会复制它,因为客户端没有为其发送值。请注意,它将复制整数值为零,因为我们将其设置为可为空的int,并且此方法将可空int的默认值视为null,而不是零。将DTO属性声明为可为空可能不会对代码的其余部分造成太大麻烦。
请注意,此方法可以轻松使用JSON。如果您需要支持XML请求/响应,则可能需要对DataContract/DataMember
属性进行一些额外的工作,以确保正确处理空值。
答案 1 :(得分:2)
虽然esker的回复很好但我想补充一点,对于可空字段可能还不够 - 因为你不知道反序列化器或用户是否创建了那个空字段。
一种方法是查看原始请求。
另一种方法是要求用户提供额外的请求(查询字符串)参数,以清楚地指定要修补的字段。 类似于:patch_fields = name,description,field3 这种方法的好处是最终用户可以更好地控制修补,并且不会错误地覆盖一个值(因为他使用了原始实体而忘记清除某些字段)