如果我还更新时间戳属性,我应该使用PUT方法进行更新

时间:2011-04-16 13:00:22

标签: rest logging audit put

更确切地说:

根据休息样式,通常认为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

4 个答案:

答案 0 :(得分:23)

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

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

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

HTTP spec

的更新中最后阐述了完整的资源与部分资源争论的全部内容。
  

原始服务器应该拒绝任何PUT   请求包含   Content-Range标头字段,因为它   可能被误解为偏袒   内容(或可能是部分内容   这是错误的PUT作为一个   完整的代表)。部分内容   可以通过定位a来进行更新   单独识别的资源   重叠一部分的状态   更大的资源,或使用   已有的不同方法   特别定义为部分   更新(例如,PATCH   [RFC5789]中定义的方法。

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

答案 1 :(得分:7)

由于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 Request403 Forbidden(因为客户端无法修改这些值。)

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

PUT /example/123

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

服务器现在接受完整的代表,因为它不包含lastUpdatelastUser

剩下的问题是如何为客户提供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在评论中指出的那样,如果客户需要访问lastUserlastUpdate,则可以将这些资源公开为新资源,并与原始资源相关联,例如如下:

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 Millertake 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