30

更准确地说:

根据 rest 风格,通常假定 POST、GET、PUT 和 DELETE http 方法应该用于 CREATE、READ、UPDATE 和 DELETE (CRUD) 操作。

事实上,如果我们坚持 http 方法的定义,事情可能就不那么清楚了

这篇文章中,它解释了:

简而言之:当且仅当您知道资源所在的 URL 以及资源的全部内容时才使用 PUT。否则,使用 POST。

主要是因为

PUT 是一个限制性更强的动词。它需要一个完整的资源并将其存储在给定的 URL 中。如果之前有资源,则将其替换;如果没有,则创建一个新的。这些属性支持幂等性,而天真的创建或更新操作可能不支持。我怀疑这可能就是为什么 PUT 是这样定义的;这是一个幂等操作,允许客户端向服务器发送信息。

在我的情况下,我通常会发布传递所有资源数据的更新,因此我可以使用 PUT 进行更新,但每次发布更新时,我都会保存一个 LastUser 和 LastUpdate 列,其中包含进行修改的用户 ID 和操作时间。

所以我想知道你的意见,因为严格来说这两列不是资源的一部分,但它们确实阻止了操作是幂等的。

萨卢多斯

sas

4

4 回答 4

28

忽略关于 REST 样式将 CRUD 映射到 HTTP 方法的评论,这是一个很好的问题。

您的问题的答案是,是的,您可以在这种情况下自由使用 PUT,即使服务器以非幂等方式更新了资源的某些元素。不幸的是,答案背后的原因非常模糊。重要的是要了解客户端请求的意图。客户端打算用传递的值完全替换资源的内容。客户端不负责服务器执行其他操作,因此不违反 HTTP 方法的语义。

这是用于允许服务器在执行 GET 操作时更新页面计数器的原因。客户端没有要求更新,因此即使服务器选择进行更新,GET 也是安全的。

整个、完整的资源与部分资源的争论终于在HTTP 规范的更新中阐明

源服务器应该拒绝任何包含 Content-Range 头字段的 PUT 请求,因为它可能被误解为部分内容(或者可能是部分内容被错误地 PUT 作为完整表示)。部分内容更新是可能的,方法是针对一个单独标识的资源,其状态与较大资源的一部分重叠,或者使用专门为部分更新定义的不同方法(例如,[RFC5789] 中定义的 PATCH 方法)。

所以,我们应该做的事情现在很清楚了。不太清楚的是为什么存在这种只允许发送完整响应的限制。已经提出了这个问题,恕我直言,在休息讨论的这个线程中仍未得到答案。

于 2011-04-16T13:54:36.943 回答
9

由于客户LastUserLastUpdate不能修改,因此我会将它们从您的资源表示中完全删除。让我用一个例子来解释我的推理。

假设我们的典型示例 API 将在被要求提供单个资源时向客户端返回以下表示:

GET /example/123

<?xml version="1.0" encoding="UTF-8" ?>
<example>
    <id>123</id>
    <lorem>ipsum</lorem>
    <dolor>sit amet</dolor>
    <lastUser uri="/user/321">321</lastUser>
    <lastUpdate>2011-04-16 20:00:00 GMT</lastUpdate>
</example>

如果客户端想要修改资源,它可能会获取整个表示并将其发送回 API。

PUT /example/123

<?xml version="1.0" encoding="UTF-8" ?>
<example>
    <id>123</id>
    <lorem>foobar</lorem>
    <dolor>foobaz</dolor>
    <lastUser>322</lastUser>
    <lastUpdate>2011-04-16 20:46:15 GMT+2</lastUpdate>
</example>

由于 API 为lastUserlastUpdate自动生成值并且不能接受客户端提供的数据,因此最合适的响应是400 Bad Requestor 403 Forbidden(因为客户端无法修改这些值)。

如果我们想符合 REST 并在执行 PUT 请求时发送资源的完整表示,我们需要从资源的表示中删除lastUserlastUpdate。这将允许客户端通过 PUT 发送完整的实体:

PUT /example/123

<?xml version="1.0" encoding="UTF-8" ?>
<example>
    <id>123</id>
    <lorem>foobar</lorem>
    <dolor>foobaz</dolor>
</example>

现在服务器将接受一个完整的表示,因为它不包含lastUpdateand lastUser

剩下的问题是如何为客户提供访问lastUpdatelastUser. 如果他们不需要它(这些字段只是 API 内部需要的),我们很好,我们的解决方案是完全 RESTful 的。但是,如果客户端需要访问这些数据,最简洁的方法是使用 HTTP 标头:

GET /example/123

...
Last-Modified: Sat, 16 Apr 2011 18:46:15 GMT
X-Last-User: /user/322
...

<?xml version="1.0" encoding="UTF-8" ?>
<example>
    <id>123</id>
    <lorem>foobar</lorem>
    <dolor>foobaz</dolor>
</example>

使用自定义 HTTP 标头并不理想,因为需要教用户代理如何阅读它。如果我们想以更简单的方式为客户提供对相同数据的访问,我们唯一能做的就是将数据放入表示中,我们面临的问题与您最初的问题相同。我至少会尝试以某种方式减轻它。如果 API 使用的内容类型是 XML,我们可以将数据放入节点属性中,而不是直接暴露为节点值,即:

GET /example/123

...
Last-Modified: Sat, 16 Apr 2011 18:46:15 GMT
...

<?xml version="1.0" encoding="UTF-8" ?>
<example last-update="2011-04-16 18:46:15 GMT" last-user="/user/322">
    <id>123</id>
    <lorem>foobar</lorem>
    <dolor>foobaz</dolor>
</example>

这样,我们至少可以避免客户端尝试在后续 PUT 请求中提交所有 XML 节点的问题。这不适用于 JSON,并且该解决方案仍然有点处于幂等性的边缘(因为 API 在处理请求时仍然必须忽略 XML 属性)。

更好的是,正如Jonah在评论中指出的那样,如果客户需要访问lastUserand lastUpdate,这些可以作为新资源公开,与原始资源链接,例如如下:

GET /example/123

<?xml version="1.0" encoding="UTF-8" ?>
<example>
    <id>123</id>
    <lorem>foobar</lorem>
    <dolor>foobaz</dolor>
    <lastUpdateUri>/example/123/last-update</lastUpdateUri>
</example>

... 进而:

GET /example/123/last-update

<?xml version="1.0" encoding="UTF-8" ?>
<lastUpdate>
    <resourceUri>/example/123</resourceUri>
    <updatedBy uri="/user/321">321</updatedBy>
    <updatedAt>2011-04-16 20:00:00 GMT</updatedAt>
</lastUpdate>

(上面也可以很好地扩展以提供包含单个更改的完整审核日志,前提是资源更改日志可用。)

请注意:
我同意Darrel Miller对这个问题的,但我想在此基础上提供一种不同的方法。请注意,这种方法不受任何标准/RFC/等的支持,它只是对问题的不同看法。

于 2011-04-16T19:10:06.700 回答
4

使用 PUT 创建资源的缺点是客户端必须提供代表它正在创建的对象的唯一 ID。虽然客户端通常可以生成此唯一 ID,但大多数应用程序设计人员更喜欢他们的服务器(通常通过他们的数据库)创建此 ID。在大多数情况下,我们希望我们的服务器控制资源 ID 的生成。那么我们该怎么办?我们可以切换到使用 POST 而不是 PUT。

所以:

放=更新

发布 = 插入

希望这对您的具体情况有所帮助。

于 2011-04-16T13:03:54.970 回答
0

HTTP 方法 POST 和 PUT 不是 CRUD 的创建和更新的 HTTP 等效方法。它们都有不同的用途。在某些情况下,使用 PUT 创建资源或使用 POST 更新资源是很有可能的、有效的甚至是首选的。

当您可以通过特定资源完全更新资源时,请使用 PUT。例如,如果您知道一篇文章位于http://example.org/article/1234,您可以直接通过此 URL 上的 PUT 来 PUT 这篇文章的新资源表示。

如果您不知道实际的资源位置,例如,当您添加一篇新文章时,但不知道将其存储在哪里,您可以将其 POST 到一个 URL,让服务器决定实际的 URL。

于 2016-09-09T17:46:09.290 回答