以 RESTful 方式增加资源计数器:PUT 与 POST

发布于 2024-08-04 14:33:35 字数 588 浏览 8 评论 0 原文

我有一个带有计数器的资源。举例来说,我们将资源称为配置文件,计数器是该资源的浏览次数轮廓。

根据 REST wiki,PUT 请求应用于资源创建或修改,并且应该是幂等的。如果我要更新配置文件的名称,那么这种组合就很好,因为我可以发出 PUT 请求,将名称设置为某个值 1000 次,结果不会改变。

对于这些标准 PUT 请求,我让浏览器执行以下操作:

PUT /profiles/123?property=value&property2=value2

为了递增计数器,可以像这样调用 url:

PUT /profiles/123/?counter=views

每次调用都会导致计数器递增。从技术上讲,这是一个更新操作,但它违反了幂等性。

我正在寻找指导/最佳实践。您只是将其作为 POST 进行吗?

I have a resource that has a counter. For the sake of example, let's call the resource profile, and the counter is the number of views for that profile.

Per the REST wiki, PUT requests should be used for resource creation or modification, and should be idempotent. That combination is fine if I'm updating, say, the profile's name, because I can issue a PUT request which sets the name to something 1000 times and the result does not change.

For these standard PUT requests, I have browsers do something like:

PUT /profiles/123?property=value&property2=value2

For incrementing a counter, one calls the url like so:

PUT /profiles/123/?counter=views

Each call will result in the counter being incremented. Technically it's an update operation but it violates idempotency.

I'm looking for guidance/best practice. Are you just doing this as a POST?

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

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

发布评论

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

评论(4

忆伤 2024-08-11 14:33:35

我认为正确的答案是使用 PATCH。我没有看到其他人建议它应该用于原子地递增计数器,但我相信 RFC 2068 说得很好:

PATCH 方法与 PUT 类似,只是实体包含一个
资源原始版本之间的差异列表
由Request-URI和所需的资源内容标识
应用 PATCH 操作后。差异列表是
采用由实体的媒体类型定义的格式(例如,
“application/diff”)并且必须包含足够的信息以允许
服务器重新创建转换原始数据所需的更改
将资源版本更改为所需版本。

因此,要更新配置文件 123 的观看次数,我会:

PATCH /profiles/123 HTTP/1.1
Host: www.example.com
Content-Type: application/x-counters

views + 1

其中 x-counters 媒体类型(我刚刚编写的)由多行字段运算符标量 元组组成。 views = 500views - 1views + 3 在语法上都是有效的(但在语义上可能被禁止)。

我可以理解一些人对另一种媒体类型的不满,但我谦虚地建议它比 POST / PUT 替代方案更正确。为一个字段创建一个资源,包含它自己的 URI,尤其是它自己的详细信息(我并没有真正保留,我拥有的只是一个整数)对我来说听起来是错误且麻烦的。如果我有 23 个不同的计数器需要维护怎么办?

I think the right answer is to use PATCH. I didn't see anyone else recommending it should be used to atomically increment a counter, but I believe RFC 2068 says it all very well:

The PATCH method is similar to PUT except that the entity contains a
list of differences between the original version of the resource
identified by the Request-URI and the desired content of the resource
after the PATCH action has been applied. The list of differences is
in a format defined by the media type of the entity (e.g.,
"application/diff") and MUST include sufficient information to allow
the server to recreate the changes necessary to convert the original
version of the resource to the desired version.

So, to update profile 123's view count, I would:

PATCH /profiles/123 HTTP/1.1
Host: www.example.com
Content-Type: application/x-counters

views + 1

Where the x-counters media type (which I just made up) is made of multiple lines of field operator scalar tuples. views = 500 or views - 1 or views + 3 are all valid syntactically (but may be forbidden semantically).

I can understand some frowning-upon making up yet another media type, but I humbly suggest it's more correct than the POST / PUT alternative. Making up a resource for a field, complete with its own URI and especially its own details (which I don't really keep, all I have is an integer) sounds wrong and cumbersome to me. What if I have 23 different counters to maintain?

牵你手 2024-08-11 14:33:35

另一种方法可能是向系统添加另一个资源来跟踪个人资料的查看情况。您可以将其称为“查看”。

要查看个人资料的所有观看次数:

GET /profiles/123/viewings

要向个人资料添加观看次数:

POST /profiles/123/viewings #here,您需要在请求正文中使用自定义媒体类型提交详细信息。

要更新现有查看:

PUT /viewings/815 # 使用您创建的自定义媒体类型在请求正文中提交修改后的查看属性。

要深入查看查看的详细信息:

GET /viewings/815

要删除查看:

DELETE /viewings/815

另外,因为您要求最佳实践,所以请确保您的 RESTful 系统是 超文本驱动

大多数情况下,在 URI 中使用查询参数并没有什么问题 - 只是不要让您的客户认为他们可以操纵它们。

相反,创建一个体现参数试图建模的概念的媒体类型。为这种媒体类型指定一个简洁、明确且具有描述性的名称。然后记录该媒体类型。在 REST 中公开查询参数的真正问题在于,这种做法通常会导致带外通信,从而增加客户端和服务器之间的耦合。

然后给你的系统一个统一的界面。例如,添加新资源始终是 POST。更新资源始终是 PUT。删除是DELETE,获取是GET。

REST 最难的部分是理解媒体类型如何融入系统设计(这也是 Fielding 由于时间不够而在他的论文中省略的部分)。如果您想要使用和记录媒体类型的超文本驱动系统的特定示例,请参阅 Sun Cloud API

An alternative might be to add another resource to the system to track the viewings of a profile. You might call it "Viewing".

To see all Viewings of a profile:

GET /profiles/123/viewings

To add a viewing to a profile:

POST /profiles/123/viewings #here, you'd submit the details using a custom media type in the request body.

To update an existing Viewing:

PUT /viewings/815 # submit revised attributes of the Viewing in the request body using the custom media type you created.

To drill down into the details of a viewing:

GET /viewings/815

To delete a Viewing:

DELETE /viewings/815

Also, because you're asking for best-practice, be sure your RESTful system is hypertext-driven.

For the most part, there's nothing wrong with using query parameters in URIs - just don't give your clients the idea that they can manipulate them.

Instead, create a media type that embodies the concepts the parameters are trying to model. Give this media type a concise, unambiguous, and descriptive name. Then document this media type. The real problem of exposing query parameters in REST is that the practice often leads out-of-band communication, and therefore increased coupling between client and server.

Then give your system a uniform interface. For example, adding a new resource is always a POST. Updating a resource is always a PUT. Deleting is DELETE, and getiing is GET.

The hardest part about REST is understanding how media types figure into system design (it's also the part that Fielding left out of his dissertation because he ran out of time). If you want a specific example of a hypertext-driven system that uses and doucuments media types, see the Sun Cloud API.

回心转意 2024-08-11 14:33:35

我认为 Yanic 和 Rich 的方法都很有趣。 PATCH 不需要是安全的或独立的,但可以是为了对并发更加鲁棒。 Rich 的解决方案在“标准”REST API 中当然更易于使用。

请参阅 RFC5789

PATCH 既不安全也不幂等,如 [RFC2616] 部分所定义
9.1.

PATCH 请求可以以幂等的方式发出,
这也有助于防止两个人之间的碰撞产生不良后果
在相似的时间范围内对同一资源发出 PATCH 请求。
来自多个 PATCH 请求的冲突可能比
PUT 冲突,因为某些补丁格式需要从
已知基点,否则它们会破坏资源。

I think both approaches of Yanic and Rich are interresting. A PATCH does not need to be safe or indempotent but can be in order to be more robust against concurrency. Rich's solution is certainly easier to use in a "standard" REST API.

See RFC5789:

PATCH is neither safe nor idempotent as defined by [RFC2616], Section
9.1.

A PATCH request can be issued in such a way as to be idempotent,
which also helps prevent bad outcomes from collisions between two
PATCH requests on the same resource in a similar time frame.
Collisions from multiple PATCH requests may be more dangerous than
PUT collisions because some patch formats need to operate from a
known base-point or else they will corrupt the resource.

感情废物 2024-08-11 14:33:35

在评估了之前的答案之后,我认为 PATCH 是不合适的,并且就我的目的而言,为一项琐碎的任务摆弄 Content-Type 违反了 KISS 原则。我只需要增加 n+1,所以我这样做了:

PUT /profiles/123$views
++

其中 ++ 是消息正文,控制器将其解释为将资源增加 1 的指令。

我选择 $ 来分隔资源的字段/属性,因为它是 合法的子分隔符,就我的目的而言,似乎比 / 更直观,在我看来,它具有可遍历性的氛围。

After evaluating the previous answers I decided PATCH was inappropriate and, for my purposes, fiddling around with Content-Type for a trivial task was a violation of the KISS principle. I only needed to increment n+1 so I just did this:

PUT /profiles/123$views
++

Where ++ is the message body and is interpreted by the controller as an instruction to increment the resource by one.

I chose $ to deliminate the field/property of the resource as it is a legal sub-delimiter and, for my purposes, seemed more intuitive than / which, in my opinion, has the vibe of traversability.

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