为需要为每个请求保存数据的 REST API 的端点建模

Posted

技术标签:

【中文标题】为需要为每个请求保存数据的 REST API 的端点建模【英文标题】:Modelling a endpoint for a REST API that need save data for every request 【发布时间】:2021-09-25 18:27:58 【问题描述】:

前段时间,我参加了一次采访,其中有一个关于 REST 建模以及如何实现它的最佳方式的问题。问题是:

您有一个 REST API,您可以在其中公开一个方法来查询两点之间的距离,尽管您必须将每个请求保存到此方法以公开请求历史记录。

我被问到在这种情况下应该使用哪种 HTTP 方法,对我来说,当时的逻辑答案是 GET 方法(执行这两个操作)。之后面试官问我为什么,因为我们也在存储请求,所以这个端点不再是幂等的,之后我就无法回复了。由于这仍然在我的脑海中,所以我决定在这里验证一下,看看其他人对这种情况应该使用哪种方法(或者有多少,例如 GET 和 POST)的意见。

【问题讨论】:

【参考方案1】:

您有一个 REST API,您可以在其中公开一个方法来查询两点之间的距离,尽管您必须将每个请求保存到此方法以公开请求历史记录。

您将如何在网络上执行此操作?您可能有一个带有表单的网页,并且该表单将具有用于收集起点和终点的输入控件。当您提交表单时,浏览器将使用控件中的数据以及表单元数据和标准 html 处理规则来创建将发送到服务器的请求。

从技术上讲,您可以使用 POST 作为表单的方法。这样做是完全合法的。但是,由于请求的语义是“有效只读”,因此更好的选择是使用 GET。

更准确地说,这意味着拥有一系列相似的资源,其表示包括有关查询字符串中描述的两个点的信息。

类似的资源系列可能会在您的源服务器上实现作为单个操作/路由,解析器从查询字符串中提取两个点并将它们作为参数传递给函数.

面试官问我为什么,因为我们也在存储请求,所以这个端点不再是幂等的了

这可能是错误的反对意见 - GET 请求的语义是安全的(实际上是只读的)。所以面试可能会争辩说,保存请求历史不是只读的。但是,这个反对是无效的,因为语义约束适用于请求消息,而不是实现。

例如,您可能已经注意到 HTTP 服务器通常会在其访问日志中为每个请求添加一个条目。显然这不是“只读的”——而只是一个实现细节;客户的请求没有说“也记录这个”。

GET 在这里仍然没问题,即使服务器正在写东西。

一个可能的反对意见是,如果我们使用 GET,那么有时缓存会返回先前的响应,而不是将请求一直传递到源服务器以进行记录。这太棒了 - 缓存是 Web 可以实现 Web 规模的重要原因。

但如果你不想缓存,正确的处理方法是在响应中添加元数据以禁止缓存,而不是更改 HTTP 方法。


另一种可能性,更符合面试官的“幂等”言论,他们希望这个“请求历史”成为客户可以编辑的资源,而查找距离将是该编辑的副作用过程。

例如,我们可能有某种“行程”资源,其中包含由客户提供的一条或多条行程。每次客户端修改行程(例如,添加另一条腿)时,都会自动调用距离查找方法。

在这种问题中,客户端正在(逻辑上)编辑资源,请求不再“有效地只读”。所以 GET 不作为一种选择,我们必须研究其他可能性。

TL;DR 版本是 POST 总是可以接受的(这就是我们在 Web 上的做法),但您可能更喜欢客户端在本地编辑资源表示的 API 样式,在这种情况下您可以让客户端在 PUT 和 PATCH 之间进行选择。

【讨论】:

首先感谢您抽出宝贵的时间来回答,这真的是完整的解释,现在对我来说看起来更清楚了。你的解释是对这个文档restfulapi.net/http-methods的一个很好的补充。

以上是关于为需要为每个请求保存数据的 REST API 的端点建模的主要内容,如果未能解决你的问题,请参考以下文章

我是否需要为SPA保护仅支持GET的REST API

将 api 密钥传递给 rest api

在 N 个并行保存请求后 Spring Boot Rest Api 卡住了

Spring Rest API 拦截器在每个/每个请求上添加响应标头

REST API:请求正文为 JSON 或纯 POST 数据?

rest - 将用户限制在自己的数据中的方法