更确切地说:
根据休息样式,通常认为POST,GET,PUT和DELETE http方法应该用于CREATE,READ,UPDATE和DELETE(CRUD)操作。
事实上,如果我们坚持使用http方法定义,事情可能就不那么清楚了
在this article中解释说:
简而言之:当且仅当您知道资源所在的URL以及资源的全部内容时才使用PUT。否则,请使用POST。
主要是因为
PUT是一个限制性更强的动词。它需要一个完整的资源并将其存储在给定的URL中。如果之前有资源,则将其替换;如果没有,则创建一个新的。这些属性支持幂等性,天真的创建或更新操作可能不支持。我怀疑这可能是为什么PUT的定义方式;它是一种幂等操作,允许客户端向服务器发送信息。
在我的情况下,我通常会发布传递所有资源数据的更新,因此我可以使用PUT进行更新,但每次发出更新时,我都会保存一个LastUser和LastUpdate列,其中包含进行修改的用户ID和时间od操作。
所以我想知道你的意见,因为严格来说这两列并不是资源的一部分,但它们确实阻止了操作是幂等的。
saludos
SAS
答案 0 :(得分:23)
忽略关于REST样式将CRUD映射到HTTP方法的注释,这是一个很好的问题。
您的问题的答案是,是的,您可以在此方案中自由使用PUT,即使服务器以非幂等方式更新资源的某些元素。不幸的是,答案背后的理由很模糊。重要的是要了解客户端请求的意图。客户端打算用传递的值完全替换资源的内容。客户端不负责服务器执行其他操作,因此不会违反HTTP方法的语义。
这是用于在执行GET操作时允许服务器更新页计数器的原因。客户端没有要求更新,因此即使服务器选择进行更新,GET也是安全的。
的更新中最后阐述了完整的资源与部分资源争论的全部内容。原始服务器应该拒绝任何PUT 请求包含 Content-Range标头字段,因为它 可能被误解为偏袒 内容(或可能是部分内容 这是错误的PUT作为一个 完整的代表)。部分内容 可以通过定位a来进行更新 单独识别的资源 重叠一部分的状态 更大的资源,或使用 已有的不同方法 特别定义为部分 更新(例如,PATCH [RFC5789]中定义的方法。
所以,我们应该做的事情现在已经清楚了。不太清楚的是,为什么只允许发送完整的响应存在这种限制。这个问题已经被问到,恕我直言,在休息讨论的这个帖子中仍然没有答案。
答案 1 :(得分:7)
由于LastUser
和LastUpdate
无法由客户端修改,我会将其从资源的表示中删除。让我用一个例子来解释我的推理。
假设我们的典型示例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会自动生成lastUser
和lastUpdate
的值,并且无法接受客户提供的数据,因此最合适的响应将是400 Bad Request
或403 Forbidden
(因为客户端无法修改这些值。)
如果我们希望在执行PUT请求时符合REST并发送资源的完整表示,我们需要从资源的表示中删除lastUser
和lastUpdate
。这将允许客户通过PUT发送完整的实体:
PUT /example/123
<?xml version="1.0" encoding="UTF-8" ?>
<example>
<id>123</id>
<lorem>foobar</lorem>
<dolor>foobaz</dolor>
</example>
服务器现在接受完整的代表,因为它不包含lastUpdate
和lastUser
。
剩下的问题是如何为客户提供lastUpdate
和lastUser
的访问权限。如果他们不需要它(并且这些字段仅在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在评论中指出的那样,如果客户需要访问lastUser
和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的take on the question,但我想提供一种不同的方法。请注意,这种方法不会被任何标准/ RFC /等备份,这只是对问题的不同看法。
答案 2 :(得分:4)
使用PUT创建资源的缺点是客户端必须提供表示它正在创建的对象的唯一ID。虽然客户端通常可以生成此唯一ID,但大多数应用程序设计人员更喜欢他们的服务器(通常通过其数据库)创建此ID。在大多数情况下,我们希望服务器控制资源ID的生成。那么我们该怎么办?我们可以切换到使用POST而不是PUT。
所以:
Put = UPDATE
发布= INSERT
希望这有助于您的具体案例。
答案 3 :(得分:0)
HTTP方法POST和PUT不是CRUD创建和更新的HTTP等价物。它们都有不同的用途。在某些情况下,使用PUT创建资源或使用POST更新资源是非常有可能的,有效甚至是首选。
当您可以通过特定资源完全更新资源时使用PUT。例如,如果您知道文章位于http://example.org/article/1234,则可以通过此URL上的PUT直接输入本文的新资源表示。
如果您不知道实际的资源位置,例如,当您添加新文章但又不知道在哪里存储它时,您可以将其发布到URL,并让服务器决定实际的URL