REST集合集合,促进和降级

时间:2019-05-15 08:04:17

标签: rest api

我们有一个称为任务的资源。通过以下端点,我们可以列出所有任务,并创建任务:

GET:  /tasks
POST: /tasks

问题在于任务可以包含子任务,我们希望嵌入功能以支持将子任务升级为自己的任务,以及将任务降级为另一个任务的子任务。

天真的方法应该是删除子任务,然后再次将其创建为任务,反之亦然,但是我们发现这有点天真。

我们想到的第二个选项是支持以下端点,其中{id}是任务的ID,而{sid}是子任务的ID:

POST: /tasks/{id}/add/{sid}
POST: /tasks/{id}/upgrade/{sid}

第一个端点应将一个任务添加到另一个任务,从而删除第一个任务,并将副本作为子任务添加到第二个任务。第二端点应将子任务从另一个任务中升级到一个任务,从而删除该子任务并将副本添加为任务。

所以我们的问题是,这是否被认为是一种好的做法,或者还有其他我们未考虑的选择?

3 个答案:

答案 0 :(得分:3)

重要的是要强调一点,REST根本不关心URI的拼写。但是,一旦REST体系结构样式的中心部分是 resource ,则URI使用名词代替动词。

REST架构样式是独立于协议的,但是通常在HTTP协议的顶部实现。在这种方法中,HTTP方法旨在表示将在给定URI标识的资源上执行的操作


您的问题没有说明您的域是什么样的。但是,让我们考虑一下您的情况:

+-------------------+
|       Task        |
+-------------------+
|- id: Long         |
|- title: String    |
|- parent: Task     |
|- children: Task[] |
+-------------------+

创建任务可以用POST请求表示:

POST /tasks
Host: example.org
Content-Type: application/json

{
  "title": "Prepare dinner"
}
HTTP/1.1 201 Created
Location: /tasks/1

创建子任务可以用一个POST请求来表示,该请求指示有效负载中的parentId

POST /tasks
Host: example.org
Content-Type: application/json

{
  "parentId": 1
  "title": "Order a pizza"
}
HTTP/1.1 201 Created
Location: /tasks/2

使用PATCH请求将parentId设置为null,可以将子任务提升为任务:

PATCH /tasks/2
Host: example.org
Content-Type: application/json-patch+json

[
  { 
    "op": "replace", "path": "/parentId", "value": null
  }
]
HTTP/1.1 204 No Content

通过使用PATCH请求设置parentId,可以将任务更新为子任务:

PATCH /tasks/2
Host: example.org
Content-Type: application/json-patch+json

[
  { 
    "op": "replace", "path": "/parentId", "value": 1 
  }
]
HTTP/1.1 204 No Content

以上示例将JSON Patchapplication/json-patch+json)用作PATCH的有效负载。另外,您可以考虑使用JSON Merge Patchapplication/merge-patch+json)。一旦将使这个答案过长,我就不会介绍这些格式的差异。但是您可以单击上面的链接,然后亲自检查它们。

有关处理错误,请参阅error handling(定义{{​​3}}方法的文档)的RFC 5789部分。


我也很感谢一些API出于某些原因而避免使用PATCH,这超出了此答案的范围。如果是这样,您可以考虑使用PATCH。在这种方法中,资源状态将用请求有效负载中发送的表示形式中定义的状态替换

或者,您可以有一个/tasks/{id}/parent这样的端点,它支持PUT。通过这种方法,客户端只需发送父代的表示(例如id),而不是任务的完整表示。

选择最适合您的方法。

答案 1 :(得分:2)

REST是客户端支持的表示形式的资源当前状态的缩写。由于REST是万维网的概括,因此您在Web上使用的相同概念也适用于遵循REST体系结构模型的应用程序。因此,基本问题解决了:您将如何设计可在Web页面上使用的系统并将相同的步骤应用于您的设计。

正如Cassio所提到的那样,URI的拼写对客户端来说并不重要,因为URI仍然是URI,您无法从URI推断出系统是否为“ RESTful”。实际上,不存在“ RESTful”或“ RESTless” URI之类的东西,因为如上所述,URI仍然是URI。最好将URI视为用于在本地或中间缓存中缓存响应的键。菲尔丁不仅做出了support of caching even a constraint的选择,而且做出了选择。

由于REST并不是一种协议,而只是一种体系结构样式,因此您基本上没有义务严格执行它,尽管您肯定会错过承诺的好处,例如将客户端与API分离,自由地开发API。服务器端,而不会破坏客户端,并且通常会使客户端在更改方面更强大。菲尔丁甚至说applications that violate the constraints he put on REST shouldn't be termed REST,以避免造成混乱。

我不同意user991710的一项评论,即REST无法用于表示流程,但我也同意REST也不应尝试创建新的动词。如前所述,REST是关于以受支持的表示形式传输资源的当前状态。如果任务可以表示为数据流,那么也可以将其呈现给客户端。消息的自我描述性,即通过使用定义有关如何处理数据有效负载的规则的媒体类型,可以确保客户端能够理解数据。即您的浏览器能够渲染图像,播放视频,显示文本和类似内容,因为它知道如何相应地解释数据流。可以通过特殊的插件或插件添加对此类字段的支持,这些插件或插件可以在运行时动态加载,而无需重新启动应用程序。

如果我必须为Web页面设计任务,那么最初我将返回一个现有任务的分页视图,可能是在表格中呈现,并带有指向能够创建新任务的页面的链接或每行的链接更新或删除现有任务。新建页面和更新页面可以使用相同的HTML表单来输入或更新任务信息。如果应将一个任务作为子任务分配给其他任务,则可以从一组给定的任务中选择父任务,即在下拉文本字段中,或在专用领域。在提交任务时,将使用HTTP方法POST来根据服务器自身的语义执行操作,因此,创建新资源还是更新一个或多个资源取决于服务器。最典型的是,服务器可以教客户可能做的所有事情。用于添加新任务或更新现有任务的表单仅通知客户端服务器支持(或期望)哪些字段。客户端实际上并不需要外部的带外知识来执行请求。服务器仍然可以拒绝不完整的有效负载或违反某些约束的有效负载,客户端将通过接收适当的错误消息来知道。

由于客户端不应该解析或解释URI,因此它们将使用某些描述URI作用的文本。在浏览器中,垃圾箱的图片可以用作删除的符号,而铅笔可以用作更新现有条目(或类似条目)的符号。作为人类,我们很快就意识到了这些链接的用途,而不必阅读实际URI的字符。

最后两段只是总结了普通Web页面上的交互模型。 REST体系结构中也应使用相同的概念。与老兄Web相比,您交换的内容可能会有更多变化,最好是使用standardized representation formats,尽管仍然使用链接的概念来指代从一种资源到其他资源的内容,并且服务器会在此向客户传授客户端所需的内容。但是,与HTML相比,您可以使用更多的HTTP方法,而不仅仅是POSTGET。与DELETE方法相比,POST方法可能更有意义。另外,根据情况,对于更新PUTPATCH可能更有意义。与使用明智的图片来提示用户该链接可能有什么好处相反,应使用链接关系名称来暗示客户端有关链接目的的信息。此类链接关系名称应为standardized,或至少表达常识,例如通过特殊的ontologies表示,或使用absolute URIs as extension mechanism

您可以添加专用链接,以基于某些过滤器(即,所有任务或仅父任务,而不是父任务)显示所有任务的集合,以便客户端可以迭代其感兴趣的任务。选择任务时,您可以添加链接到客户端可以调用以了解这些子任务(如果有兴趣)的所有子任务。此处任务的URI可能保持不变,默认情况下支持缓存。请注意,关于如何将数据存储在后端中,我没有提及任何内容,因为这只是客户端通常不感兴趣的一些实现细节。这只是一个完美的情况,其中域模型不必类似于资源状态表示。

关于执行任务升级或降级的HTTP操作基本上是一些设计选择,该选择还可能取决于交换有效负载的表示格式。由于HTTP仅支持POSTGET,因此可以通过POST请求这种更改,其他媒体类型可能支持其他HTTP methods,例如PUT,根据其specification (last paragraph page 27)允许具有副作用或PATCH,这些副作用需要原子应用-完全或完全不应用。 PATCH实际上类似于修补软件,在修补软件中,应将一组指令应用于某些目标代码。威廉·杜兰德(William Durand)在引文blog-post中总结了这一概念。但是,他后来在更新博客文章时提到,通过application/merge-patch+json,可以使用一种更自然,更直观的“更新”资源的方式。关于形式支持,存在hal-formshalo-json (halform)Ionhydra之类的草稿,它们提供了这样的定义,但目前缺乏更广泛的库支持或最终版本。标准。因此,尚需将一些工作纳入公认的标准。

结束语,您应该像设计系统那样设计适用于Web页面的API,在典型Web页面上应用与交互所用的相同概念(例如链接和表单),并在您的回应。使用哪种HTTP操作执行实际的升级或降级,可能取决于实际的媒体类型及其支持的HTTP操作。 POST在所有情况下均应适用,PUT对于以Web表单完成的典型更新可能更直观,而修补将需要您的客户实际计算将当前资源的表示形式转换为所需的表示形式所需的步骤。 (如果您特别使用application/json-patch+json)。 application/merge-patch+json也可能适用,这将显着简化修补程序,因为表单中包含的当前数据可以直接发送到服务器,而默认规则将决定是否删除,添加或更新字段。

答案 2 :(得分:0)

您可以将路线分为:

POST : /tasks => for create or add tasks 

PUT/PATCH : /tasks => for upgrading your tasks

,或者甚至更具体,您可以将:id传递给params。我认为最好的方法是每当需要更改,删除更新或获取特定对象时,必须在参数中使用:id

PUT/PATCH/GET : /tasks/:id