这两个RESTfulAPI哪个设计更好?

Which of these two RESTful API design is better?

我正在为 "jobs" 这样的概念设计一组 RESTful API。作业的模型如下所示:

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

并遵循 RESTful 原则,客户端通过 /api/jobs 上的 HTTP 动词增删改查作业。例如,初始化一个新工作是

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
}

我的问题是,我应该如何为 "pause" 这样的操作设计 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
}

考虑到以后我可能会实现"resume"、"prioritize"、"deprioritize"等其他操作,你觉得这两个方案哪个更好?或者有更好的做法吗?

这个问题有几个方面,选择的选项取决于您要将哪些事情设置为优先级。

问题 1) 从 REST 的角度来看哪个更好?

从严格的 REST 角度来看,PATCH{ "status": "pause" } 是正确的。 REST 是代表性状态转移。而这个解决方案最严格地符合那个。

问题 2) 我应该使用 'action' 定向端点吗?

从严格的 REST 角度来看,答案是否定的。尽管如此,在实践中有很多很好的理由这样做。 strictly REST API 实现的副作用之一是您的 API 可以开始看起来像一个可通过网络访问的数据存储,而不是一个自我-包含服务。发生这种情况时通常会发生越来越多的逻辑迁移到 API 的客户端...随着对不同 REST 操作的多次调用,复杂的操作在客户端代码中实现。

这通常不是您想要的,虽然可以通过精心设计 API 来避免,但大多数情况下并非如此。

问题 3) 最佳做法是什么?

没有,至少客观上没有。具有不同目的的不同 API 更适合 REST 或面向操作的调用机制。就是说,我很少遇到 API 完全是 RESTful 并且没有面向行动的例程......而且大多数人都会随着时间的推移而漂移......如果没有其他原因的话大多数 API 都需要某种搜索功能,而严格的 REST 没有为此提供任何规定。

所以,最后,我的建议是,在它起作用的地方使用 REST,它会让你的 API 更容易让其他人开始使用......并在操作不正确时使用面向操作的端点' 非常适合 REST 概念。最好有一个易于理解的动作端点,而不是一个难以掌握的在幕后触发神奇事物的 REST 动作。

但这就像......我的意见,伙计。

My question is, how should I design the API for an action like "pause"?

您将如何设计一个网站来支持像 "pause" 这样的操作?

您可能会从一个客户可以获取的目标网页开始。您 return 的响应可能会表示作业的当前状态,并且还会有 links,带有用户可以理解的语义注释。其中之一是 "pause" 操作。

暂停后 link 可能会得到一个表单,其中包含多个输入字段、每个字段的语义注释以及每个字段的默认值。客户将替换输入字段中的部分或全部值,然后提交表单。

这会将表单数据 POST 发送到您指定的端点,此时您的实现将着手调用暂停副作用,并以描述结果的表示形式进行响应。

这就是要遵循的模式。您的 REST-API 的实现是一个适配器,使您的服务看起来像一个通用网站。

这里的关键思想是客户端不需要提前知道要使用的 URI 或要使用的 http 方法,因为该信息已编码到所提供的 link 的表示中由服务器。客户端只需要识别并遵循 links.

POST 是 很好; HTML 客户自 HTTP/1.0 以来一直在使用它。 PUT 和 PATCH 也 fine,如果你想提供一个 "remote authoring" 接口。

您可能想要查看 Rest Causistry. Make sure that you go through the comments, which is where the real discussion of REST takes place. It's also useful to review Fielding's Paper Tigers and Hidden Dragons...

I should also note that the above is not yet fully RESTful, at least how I use the term. All I have done is described the service interfaces, which is no more than any RPC. In order to make it RESTful, I would need to add hypertext to introduce and define the service, describe how to perform the mapping using forms and/or link templates, and provide code to combine the visualizations in useful ways.

Matt Timmermans'评论说得对;如果您只是记录客户端自行导航的一堆断开连接的端点,那么您就没有在做 REST。在不需要允许客户端和服务器独立发展的解决方案中,这也是 fine

REST is intended for long-lived network-based applications that span multiple organizations. If you don’t see a need for the constraints, then don’t use them.

跟进评论:

it seems that this type of navigation-based API organization is very flexible if we are talking about a human operated client (like browser), but I'm not so sure it is easy to code an automatic client that can adjust changes in the server side. "... add hypertext to introduce and define the service, describe how to perform the mapping using forms and/or link templates ..." sounds all too human-oriented to me.

我也遇到了这个问题。

没有人声称,使用 REST API,机器消费者将神奇地能够理解 语义 API 中的变化。如果我们想要支持现有客户端,那么我们在服务器上所做的更改必须以向后兼容的方式完成。但是 - 这是关键思想 - 在语义和表示之间有一个额外的间接层。

举个简单的例子,在一个带分页的API中,消费者需要理解next page的语义;它需要能够向客户端请求下一页的句柄 link。但它不需要知道 link 的表示方式、link 机制、URI 或类似内容。 通用浏览器模拟知道这些位,并为消费者完成所有工作。

消费者还需要了解一个协议;但该协议以 links 表示,而不是 URI。