假设您有一个更新文档的存储库方法:
public Document UpdateDocument(Document document)
{
Document serverDocument = _db.Documents.Find(document.Id);
serverDocument.Title = document.Title;
serverDocument.Content = document.Content;
_db.SaveChanges();
return serverDocument;
}
在这种情况下,实体有两个属性。更新文档时,JSON请求中都需要这两个属性,因此请求PUT /api/folder
的主体为
{
"documentId" = "1",
"title" = "Updated Title"
}
会返回错误,因为未提供“内容”。我这样做的原因是,即使对于用户不更新的可空属性和属性,强制客户端在请求中指定这些字段似乎更安全,以避免使用nulls服务器端覆盖未指定的字段。
这导致我总是要求PUT和POST请求中的每个可更新属性,即使这意味着为这些属性指定null。
这很酷吗,还是有一种我还没有学到的模式/练习可能会通过只发送线路上需要的内容来促进部分更新?
答案 0 :(得分:16)
API设计的最佳实践是使用HTTP PATCH进行部分更新。 事实上,像你这样的用例就是IETF首先引入它的原因。
RFC 5789非常准确地定义了它:
PATCH 用于将部分修改应用于资源。
有必要采用新方法来提高互操作性并防止发生这种情况 错误。 PUT 方法已定义为覆盖资源
使用完整的新主体,并且无法重复使用以进行部分更改。 否则,代理和缓存,甚至客户端和服务器可能会得到 混淆了操作的结果。 POST已被使用但是 没有广泛的互操作性(对于一个,没有标准的方法 发现补丁格式支持)。
Mark Nottingham撰写了一篇关于在API设计中使用PATCH的精彩文章 - http://www.mnot.net/blog/2012/09/05/patch
在你的情况下,那将是:
[AcceptVerbs("PATCH")]
public Document PatchDocument(Document document)
{
Document serverDocument = _db.Documents.Find(document.Id);
serverDocument.Title = document.Title;
serverDocument.Content = document.Content;
_db.SaveChanges();
return serverDocument;
}
答案 1 :(得分:2)
这很酷,还是有一种我没学过的模式/练习 然而,通过仅发送内容可以促进部分更新 电线需要吗?
执行POST
或PUT
的一个好方法是仅包含该特定请求所需的值。在做UpdateDocument
时你应该问自己“这里应该做什么”?如果您在该对象上有一百个字段,则需要更新所有字段或仅更新其中的一部分。你真的想做什么“行动”?
让我们举例说明这些问题,比如我们有一个User
对象,其中包含以下字段:
public class User {
public int Id {get;set;}
public string Username {get;set;}
public string RealName {get;set;}
public string Password {get;set;}
public string Bio {get;set;}
}
然后你有两个用例:
当你做那些你不会做的时候,或者最好有一个可以做到这两点的更新方法。您应该使用以下方法,而不是使用通用UpdateUser
方法:
UpdateProfile
UpdatePassword
接受他们只需要的字段的方法,仅此而已。
public User UpdateProfile(int id, string username, string realname, string bio) {
}
public User UpdatePassword(int id, string password) {
}
现在问题是:
我有一个“用户操作”允许更新的用例 多个字段,其中一些字段可以从“无输入” 用户,但我不想在我的模型中更新该字段。
假设用户更新了他/她的个人资料,并为Username
,RealName
提供了值,但未提供Bio
的值。但是如果它已经有值,你不希望将Bio
设置为null或为空。然后,这将成为应用程序业务逻辑的一部分,应该明确处理。
public User UpdateProfile(int id, string username, string realname, string bio) {
var user = db.Users.Find(id);
// perhaps a validation here (e.g. if user is not null)
user.Username = username;
user.RealName = realname;
if (!string.IsNullOrEmptyWHiteSpace(bio)) {
user.Bio = bio;
}
}