我有一个有计数器的资源。例如,让我们调用资源 个人资料 ,计数器是 视图 的数量轮廓。
根据REST wiki,PUT请求应该用于资源创建或修改,并且应该是幂等的。如果我正在更新配置文件的名称,那么这种组合很好,因为我可以发出一个PUT请求,将名称设置为1000次,结果不会改变。
对于这些标准PUT请求,我有浏览器做类似的事情:
PUT /profiles/123?property=value&property2=value2
对于递增计数器,可以像这样调用url:
PUT /profiles/123/?counter=views
每次调用都会导致计数器递增。从技术上讲,它是一种更新操作,但它违反了幂等性。
我正在寻找指导/最佳实践。你刚才这样做了吗?
答案 0 :(得分:11)
我认为正确的答案是使用PATCH。我没有看到有人推荐它应该用来原子地增加一个计数器,但我相信RFC 2068说得很好:
PATCH方法与PUT类似,只是实体包含a 资源的原始版本之间的差异列表 由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
媒体类型(我刚刚编写的)由多行field operator scalar
元组组成。 views = 500
或views - 1
或views + 3
在语法上都有效(但可能在语义上被禁止)。
我可以理解一些皱眉 - 组成另一种媒体类型,但我谦卑地建议它比POST / PUT替代方案更正确。构建一个字段的资源,完成它自己的URI,特别是它自己的细节(我没有真正保留,我只有一个整数)听起来错误和麻烦。如果我有23个不同的计数器要维护怎么办?
答案 1 :(得分:9)
另一种方法可能是向系统添加另一个资源以跟踪配置文件的查看。你可以称之为“观看”。
查看个人资料的所有观看次数:
GET / profiles / 123 / viewings
要向个人资料添加观看次数:
POST / profiles / 123 / viewings #here,您将在请求正文中使用自定义媒体类型提交详细信息。
更新现有的观看内容:
PUT / viewings / 815#使用您创建的自定义媒体类型在请求正文中提交Viewing的修订属性。
深入了解观看的详细信息:
GET / viewings / 815
删除观看次数:
DELETE / viewings / 815
另外,因为您要求进行最佳练习,请确保您的RESTful系统为hypertext-driven。
在大多数情况下,在URI中使用查询参数没有任何问题 - 只是不要让客户知道他们可以操纵它们。
相反,创建一个体现参数试图建模的概念的媒体类型。为此媒体类型提供简洁,明确和描述性的名称。然后记录此媒体类型。在REST中公开查询参数的真正问题是,这种做法经常导致带外通信,因此增加了客户端和服务器之间的耦合。
然后为您的系统提供统一的界面。例如,添加新资源始终是POST。更新资源始终是PUT。删除是DELETE,getiing是GET。
关于REST最难的部分是理解媒体类型如何影响系统设计(这也是Fielding因为时间不多而遗漏了论文的部分)。如果您想要使用和描述媒体类型的超文本驱动系统的特定示例,请参阅Sun Cloud API。
答案 2 :(得分:0)
我认为Yanic和Rich的两种方法都是有趣的。 PATCH不需要是安全的或无效的,但可以是为了更好地抵御并发。 Rich的解决方案当然更容易在“标准”REST API中使用。
请参阅RFC5789:
PATCH既不是安全的,也不是[RFC2616],Section所定义的幂等 9.1。
PATCH请求可以以幂等的方式发出, 这也有助于防止两者之间发生冲突造成的不良后果 PATCH在相似的时间范围内对同一资源进行请求。 来自多个PATCH请求的冲突可能比更危险 PUT冲突,因为一些补丁格式需要从a操作 已知的基点,否则它们会破坏资源。
答案 3 :(得分:0)
在评估了之前的答案后,我认为PATCH是不合适的,为了我的目的,摆弄Content-Type来完成一项微不足道的任务违反了KISS principle。我只需要增加n + 1,所以我就这样做了:
PUT /profiles/123$views
++
其中++
是消息正文,并由控制器解释为将资源增加一的指令。
我选择$
来消除资源的字段/属性,因为它是legal sub-delimiter,而且就我的目的而言,似乎比/
更直观,在我看来,.content > ul
可穿越性的氛围。