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

发布于 2024-11-01 22:17:31 字数 689 浏览 1 评论 0原文

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

但如果我们坚持 HTTP 方法定义,可能就不那么清晰了。

这篇文章中解释说:

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

主要是因为

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

就我而言,我通常会发布传递所有资源数据的更新,因此我可以使用 PUT 进行更新,但每次发布更新时,我都会保存 LastUserLastUpdate 列,以及进行修改的用户 ID 和操作时间。

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

According to REST style, it's generally assumed that HTTP POST, GET, PUT, and DELETE methods should be used for CREATE, READ, UPDATE and DELETE (CRUD) operations.

But if we stick to the HTTP method definitions, it might not be so clear.

In this article it's explained that:

In a nutshell: use PUT if and only if you know both the URL where the resource will live, and the entirety of the contents of the resource. Otherwise, use POST.

Mainly because

PUT is a much more restrictive verb. It takes a complete resource and stores it at the given URL. If there was a resource there previously, it is replaced; if not, a new one is created. These properties support idempotence, which a naive create or update operation might not. I suspect this may be why PUT is defined the way it is; it's an idempotent operation which allows the client to send information to the server.

In my case I usually issue updates passing all the resource data, so I could use PUT for updates, but every time I issue an update I save a LastUser and LastUpdate column, with the user id that made the modification and the time of the operation.

I'd like to know your opinion, because strictly speaking those two columns are not part of the resource, but they do prevent the operation from being idempotent.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

雨巷深深 2024-11-08 22:17:32

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

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

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

The HTTP methods POST and PUT aren't the HTTP equivalent of the CRUD's create and update. They both serve a different purpose. It's quite possible, valid and even preferred in some occasions, to use PUT to create resources, or use POST to update resources.

Use PUT when you can update a resource completely through a specific resource. For instance, if you know that an article resides at http://example.org/article/1234, you can PUT a new resource representation of this article directly through a PUT on this URL.

If you do not know the actual resource location, for instance, when you add a new article, but do not have any idea where to store it, you can POST it to an URL, and let the server decide the actual URL.

星軌x 2024-11-08 22:17:31

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

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

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

整个、完整资源与部分资源的争论终于在 HTTP 规范

源服务器应该拒绝任何 PUT
请求包含一个
Content-Range 头字段,因为它
可能会被误解为部分
内容(或者可能是部分内容
被错误地放置为
充分代表)。部分内容
更新可以通过瞄准
单独标识的资源
与一部分重叠的状态
更大的资源,或者通过使用
已经采用的不同方法
专门为部分定义
更新(例如,PATCH
[RFC5789] 中定义的方法)。

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

Ignoring the comment about the REST style mapping CRUD to the HTTP methods, this is an excellent question.

The answer to your question is, yes you are free to use PUT in this scenario even though there are some elements of the resource that are updated by the server in a non-idempotent manner. Unfortunately, the reasoning behind the answer is quite vague. The important thing, is to understand what was the intent of the client request. The client intended to completely replace the contents of resource with the values passed. The client is not responsible for the server doing other operations and therefore the semantics of the HTTP method are not violated.

This is the reasoning that is used to allow a server to update a page counter when you do a GET operation. The client didn't ask for an update therefore the GET is safe even though the server chose to make an update.

The whole, complete resource versus partial resource debate has finally been spelled out in an update to the HTTP spec

An origin server SHOULD reject any PUT
request that contains a
Content-Range header field, since it
might be misinterpreted as partial
content (or might be partial content
that is being mistakenly PUT as a
full representation). Partial content
updates are possible by targeting a
separately identified resource with
state that overlaps a portion of
the larger resource, or by using a
different method that has been
specifically defined for partial
updates (for example, the PATCH
method defined in [RFC5789]).

So, what we are supposed to do is now clear. What is not so clear is why there exists this constraint on only being allowed to send full responses. That question has been asked and IMHO remains unanswered in this thread on rest-discuss.

久光 2024-11-08 22:17:31

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

如果我们想要符合 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>

服务器将接受完整的表示,因为它不包含 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>

这样我们至少可以避免客户端尝试提交所有XML节点的问题在后续 PUT 请求中。这不适用于 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>

如果资源更改日志可用,上面的内容也可以很好地扩展以提供包含单独更改的完整审核日志。 )

请注意:
我同意达雷尔·米勒接受这个问题,但我想提供一种不同的方法在它上面。请注意,这种方法没有得到任何标准/RFC/等的支持,它只是对问题的不同看法。

As LastUser and LastUpdate are not modifiable by the client, I'd remove them from the representation of your resource altogether. Let me explain my reasoning with an example.

Let's say that our typical example API will return the following representation to the client when asked to provide a single resource:

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>

If a client wants to modify the resource, it would presumably take the whole representation and send it back to the 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>

Since the API generates values for lastUser and lastUpdate automatically and cannot accept data provided by the client, the most appropriate response would be 400 Bad Request or 403 Forbidden (since the client cannot modify these values).

If we want to be compliant with REST and send a full representation of the resource when doing a PUT request, we need to remove lastUser and lastUpdate from the representation of the resource. This will allow clients to send the full entity via PUT:

PUT /example/123

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

The server would accept a full representation now that it doesn't contain lastUpdate and lastUser.

The question that remains is how to provide clients with access to lastUpdate and lastUser. If they don't need it (and these fields are required just internally by the API), we are fine and our solution is perfectly RESTful. If however clients need access to this data, the cleanest approach would be to use HTTP headers:

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>

Using a custom HTTP header is not ideal because user agents need to be taught on how to read it. If we want to provide clients with access to the same data in a more easier way, the only thing that we can do is to put the data into the representation, and we are facing the same problem as in your original question. I would at least try to mitigate it somehow. If the content type used by the API is XML, we can put the data into node attributes instead of exposing them directly as node values, i.e.:

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>

This way we'll at least avoid the problem where a client would attempt to submit all XML nodes in a follow-up PUT request. This won't work with JSON, and the solution is still a bit on the edge of idempotency (since the API would still have to ignore the XML attributes when processing the request).

Even better, as Jonah pointed out in the comments, if clients need access to lastUser and lastUpdate, these can be exposed as a new resource, linked from the original one e.g. as follows:

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>

... and then:

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>

(The above can be also nicely expanded to provide a full audit log with individual changes, providing a resource changelog is available.)

Please note:
I agree with Darrel Miller's take on the question, but I wanted to provide a different approach on top of it. Note that this approach is not backed-up by any standards/RFCs/etc, it's just a different take on the problem.

ゃ懵逼小萝莉 2024-11-08 22:17:31

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

所以:

Put = UPDATE

Post = INSERT

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

The disadvantage of using PUT to create resources is that the client has to provide the unique ID that represents the object it is creating. While it usually possible for the client to generate this unique ID, most application designers prefer that their servers (usually through their databases) create this ID. In most cases we want our server to control the generation of resource IDs. So what do we do? We can switch to using POST instead of PUT.

So:

Put = UPDATE

Post = INSERT

Hopefully, this helps for your specific case.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文