这两种RESTful API设计,哪一种更好?

3

我正在设计一组类似于“工作”的RESTful API。一个工作的模型看起来像这样:

Job: {
  id: 1,
  name: "Brew coffee",
  status: "paused" | "running" | "finished",
  progress: 0.75
}

按照RESTful原则,客户端通过HTTP动词在/api/jobs上进行CRUD操作。例如,初始化一个新的作业:

POST /api/jobs
--------------
Request body: {
  name: "Push button"
}
---------------
Response status: 200 OK
Response body: {
  id: 2,
  name: "Cook dinner",
  status: "running",
  progress: 0
}

访问此工作的方法是

GET /api/jobs/2
--------------
Request body: {}
---------------
Response status: 200 OK
Response body: {
  id: 2,
  name: "Cook dinner",
  status: "running",
  progress: 0.5
}

我的问题是,如何为“暂停”这样的操作设计API?我考虑了两个选项:

一种选项是将其设计为PATCH请求,并直接声明我想要的最终状态,如下所示:

PATCH /api/jobs/2
--------------
Request body: {
  status: "paused"
}
---------------
Response status: 200 OK
Response body: {
  id: 2,
  name: "Cook dinner",
  status: "paused",
  progress: 0.65
}

另一种选择是将其设计为一个POST请求,并声明我想要的action,如下所示。
POST /api/jobs/2
--------------
Request body: {
  action: "pause"
}
---------------
Response status: 200 OK
Response body: {
  // same as above
}

考虑到以后可能会实现其他操作,比如“恢复”、“优先级”,“降低优先级”等,你认为下面两个选项哪个更好?或者还有更好的方法吗?

2
你问的问题的答案并不重要... 但是如果从GET /api/jobs/2的响应中没有告诉你答案,那就不是REST。很抱歉我像Roy Fielding一样说这些话,但是如果你真的不在乎是否符合REST,那就按照满足你需求的方式去做吧。 - undefined
2个回答

2
我的问题是,如何设计一个类似“暂停”的API?
如果要设计一个支持“暂停”操作的网站,你可能会从一个着陆页开始,客户端可以获取该页面。你返回的响应可能会包含当前作业状态的表示,并且还会有链接,这些链接带有用户可以理解的语义注释。其中之一将用于“暂停”操作。
点击“暂停”链接可能会获取一个表单,其中包含许多输入字段,每个字段都有语义注释,可能还有每个字段的默认值。客户端将替换某些或所有输入字段中的值,并提交表单。
这将向您指定的终端POST表单数据,此时您的实现将开始调用暂停副作用,并响应描述结果的表示。
这是要遵循的模型。 REST-API的实现是一个适配器,使您的服务看起来像一个通用的网站。
关键思想在于,客户端不需要预先知道要使用的URI或http方法,因为该信息被编码到服务器提供的链接表示中。客户端只需要识别和跟随链接即可。
"POST是可以的;自HTTP/1.0以来,HTML客户端一直在使用它。如果您想提供“远程创作”接口,PUT和PATCH也可以使用。您可能需要查看Rest Causistry。确保您浏览评论部分,那里是真正讨论REST的地方。还有有用的是回顾Fielding的Paper Tigers and Hidden Dragons...引用块中提到:“我还应该指出,上述内容尚未完全符合RESTful,至少不是我使用该术语的方式。 我所做的只是描述服务接口,这不过是任何RPC所能做到的。为了使其符合REST,我需要添加超文本来介绍和定义服务,描述如何使用表单和/或链接模板执行映射,并提供代码以有用的方式组合可视化效果。”"
Matt Timmermans的评论是正确的;如果你只是记录一堆客户端自行导航的不相关的终点,那么你并没有做REST。在不需要允许客户端和服务器独立演化的解决方案中,这也是可以接受的。
引用一下:
“REST旨在用于跨越多个组织的长期网络应用程序。如果您不认为有必要使用约束条件,则不要使用它们。”
回应评论:
“如果我们谈论的是人类操作的客户端(如浏览器),那么基于导航的API组织类型似乎非常灵活,但我不确定是否容易编写自动客户端来调整服务器端的更改。‘…添加超文本以介绍和定义服务,使用表单和/或链接模板描述如何执行映射…’听起来太针对人类了。”
我也遇到了这个问题。
没有人声称,使用REST API后,机器消费者就会神奇地理解API中的语义变化。如果我们想支持现有的客户端,那么我们在服务器上所做的更改必须以向后兼容的方式进行。但是——这是关键的想法——在语义和表示之间有一个额外的间接层。
举个简单的例子,在带有分页的API中,消费者需要理解“下一页”的语义;它需要能够请求客户端提供下一页链接的句柄。但它不需要知道关于该链接如何表示、链接机制、URI或类似的任何内容。通用的浏览器模拟器了解这些细节,并为消费者完成所有工作。
消费者仍然需要理解一种协议;但是这种协议是通过链接来表达的,而不是通过URI来表达的。

谢谢。关于RESTful设计有一个问题:这种基于导航的API组织方式对于人工操作的客户端(比如浏览器)来说非常灵活,但是我不确定编写一个能够自动适应服务器端变化的客户端是否容易。对我来说,“...添加超文本来介绍和定义服务,并使用表单和/或链接模板描述如何执行映射...”听起来过于偏向人类。 - undefined

1
这个问题有几个方面需要考虑,选择哪个选项取决于你想要设置哪些事情作为优先级。
问题1)从REST角度来看,哪个更好?
从严格的REST角度来看,使用{ "status": "pause" }PATCH是正确的。因为REST是REpresentative State Transfer,而这种解决方案最严格地符合这一点。
问题2)我应该使用“操作”为导向的端点吗?
从严格的REST角度来看,答案是否定的。但实际上,有很多非常好的理由去这样做。严格的REST API实现的一个副作用是,你的API可能开始看起来和表现得像一个可访问的数据存储,而不是一个独立的服务。当发生这种情况时,通常会发生更多的逻辑迁移到API的客户端... 复杂的操作变成了对不同REST操作的多次调用的客户端代码。
这通常不是你想要的结果,虽然可以通过精心设计的API避免,但大多数情况下并不是这样。
问题3)什么是最佳实践?
至少没有客观的标准。不同目的的API适合REST或面向动作的调用机制。话虽如此,我很少遇到完全符合REST要求且没有面向动作例程的API…而且大多数API随着时间的推移也会朝这个方向漂移……即使只是因为大多数API需要某种搜索功能,而严格的REST没有为此提供规定。
因此,我的建议是,在适当的情况下使用REST,这将使其他人更容易开始使用您的API……而当操作不适合REST概念时,请使用面向动作的端点。拥有易于理解的操作端点比触发幕后神奇事物的REST操作更容易掌握。
但这只是我的意见。

但是请确保不要使用PATCH和application/json作为请求的负载。 - undefined
能详细说明一下吗? - undefined
请参阅https://www.rfc-editor.org/errata/eid3169 - 仅使用具有为PATCH设计的语义的有效载荷媒体类型进行PATCH。这对于application/json并非如此。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接