8

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

例如,我有一个Userat ,/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'}

到目前为止,我一直认为这应该被视为一个错误,因为它意味着部分更新,但是这个答案让我想到了它,说不完整的表示仍然是给服务器的完整替代是有道理的用默认值填空的自由。

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

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

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

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

请记住,我不是在问什么感觉对或什么有意义。我在问哪一个是由标准支持的。

4

2 回答 2

5

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

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

但是,您的链接示例并不是指不传递默认值,因此它不是“不完整表示”的好示例。相反,链接不是客户端向服务器表示资源的一部分。我认为您在这里引入了另一个概念:仅从服务器返回到客户端的属性。这就是我在引发这篇文章的另一个线程上所说的。

我所说的不是不完整的表示;这是不同的表示。您实际上是在处理描述同一资源的两种不同媒体类型(即表示)。一个来自客户端(我们将其称为 application/vnd.example.api.client),另一个来自服务器(application/vnd.example.api.server)。他们可能没有明确地被标记为这样,但这至少是隐含的。因此,由于它们是两种不同的媒体类型,它们对同一资源表达不同的东西。

既然您提到了 HAL,请考虑客户端通常不会将媒体类型 application/hal+json 的消息发布到服务器。以HALTalk 的注册 rel为例。预期的内容类型是 application/json,而不是 application/hal+json。而且,如果您查看示例帖子,则没有任何关于 HAL 的内容。没有链接,没有嵌入对象等。但是...如果您随后获取从此 POST 返回的 Location 标头返回的 URL,假设您的客户端通过 JSON 接受 HAL,它将返回 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 或仅一种自定义媒体类型)框起来。然而,这并没有改变这样一个事实,即虽然它是相同的底层资源,但它们是两种不同的表示形式。说得通?

于 2013-11-07T04:43:19.833 回答
0

PUT 用于替换。服务器可以修改/增加数据,但最终表示应该是有效负载的函数,而不是最终状态。

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

于 2013-11-07T01:10:45.683 回答