使用具有不完整表示的PUT方法

时间:2013-11-07 00:44:00

标签: api http rest

PUT的预期标准行为是什么,资源表示不完整?

例如,我在User处有/api/users/1,由下面的HAL json代表:

{'id': 1,
 'username': 'joedoe',
 'email': 'joe@doe.com',
 'password_hash': '9039dmk38f84uf4029i339kf32f0932i',
 'last_visit': '2013-11-04 21:09:01',
 'public': true,
 '_links': {'self': {'href': 'http://foo.bar.com/api/users/1'}}

}

然后,我发出PUT请求更改usernameemail,其中的表示缺少其他属性:

PUT /api/users/1

{'username': 'joeydoey',
 'email': 'joey@doey.com'}

到目前为止,我一直认为这应该被视为一个错误,因为它意味着部分更新,但是this answer让我思考它,并且有理由说不完整的表示仍然是完全替换,使服务器可以自由填写默认的空白。

我在HTTP标准上找不到任何与此相关的内容,所以我不得不问,在这种情况下预期的标准化行为是什么?

  1. 它应该导致错误,因为它意味着部分更新。 PUT有效负载的模式应与使用GET检索到的相同资源和媒体类型的模式相同。

  2. 它应该会成功,因为服务器可以使用该媒体类型的默认值自由填充空白。在这种情况下,它会将密码重置为空密码或默认密码并相应地刷新散列,并将last_visit和公共值设置为默认值。当您考虑HATEOAS时,如果客户端提交服务器返回的相同媒体类型,则此选项更有意义,因为它无法预测超媒体控件将如何更改,每次客户端时,表示都必须不完整发送所有超链接,服务器必须相应地重置它们。

  3. 1和2都是有效的,因为没有标准化的行为,并且由媒体类型决定如何处理它。这感觉不对,因为PUT不是从属于资源本身,而是替换它。

  4. 请记住,我不是在问什么是正确的或什么是有道理的。我问的是标准支持哪一个。

2 个答案:

答案 0 :(得分:5)

只要PUT的结果完全取代客户对资源的理解(即,未传递的那些属性的先前值不影响它们在PUT之后的值),它应该成功。然而,它确实使得它看起来有点令人困惑,因为很多人倾向于将PUT的这种使用与字段级更新语义(而不是完全替换)相提并论。

虽然这里技术上没有违反REST约束,但传递所有值并且不使用服务器默认值可能是更好的主意,因为这有助于保持向前兼容性。默认值可能随时间而变化,因此通常应避免使用它们。

然而,你的链接示例并不是指不传递默认值,因此它不是“不完整表示”的好例子。相反,链接不是客户端向服务器表示资源的一部分。我认为你在这里混合了另一个概念:从服务器返回到客户端的属性。这就是我在引发这篇文章的另一个主题上所谈论的内容。

我所说的并不是一个不完整的表述;这是一个不同的代表。您实际上正在处理描述相同资源的两种不同媒体类型(即表示)。一个来自客户端(我们称之为application / vnd.example.api.client),另一个来自服务器(application / vnd.example.api.server)。它们可能没有明确标记为这样,但至少隐式地发生了什么。因此,由于它们是两种不同的媒体类型,因此它们表达了与相同资源不同的内容。

由于您提到了HAL,请考虑客户端通常不会将媒体类型application / hal + json的消息POST到服务器。看一下the signup rel from HALTalk的例子。预期的内容类型是application / json,而不是application / hal + json。而且,如果你看一下示例帖子,就没有任何关于它的信息。没有链接,没有嵌入对象等等。但是......如果你然后获取从这个POST返回的Location头返回的URL,假设你的客户端接受HAL over JSON,它将返回application / hal + json类型的响应(即,具有链接的用户)。两种不同的媒体类型,即同一资源的两种不同表示形式。

因此,让我用Accept和Content-Type标题来装饰你的例子来说明我的观点。

请求

PUT /api/users/1 HTTP/1.1
Content-Type: application/vnd.example.api.client+json

{'username': 'joeydoey',
 'email': 'joey@doey.com'}

<强>响应

200 OK
Content-Type: application/hal+json;profile=application/vnd.example.api.server

{'id': 1,
 'username': 'joeydoey',
 'email': 'joey@doey.com',
 'password_hash': '9039dmk38f84uf4029i339kf32f0932i',
 '_links': {'self': {'href': 'http://foo.bar.com/api/users/1'}}
}

大多数系统都没有详细说明其媒体类型。通常它只是以更通用的类型(如application / json或只有一种自定义媒体类型)构建。但是,这一切都没有改变这一事实,即虽然它是相同的底层资源,但它们是两种不同的表示形式。有意义吗?

答案 1 :(得分:0)

PUT是为了更换。服务器可以修改/扩充数据,但最终表示应该是有效载荷的函数,而不是最终状态的函数。

在您的示例中,密码哈希似乎不是服务器可以填写的内容,对吧?在这种情况下,PUT应该导致错误。