REST API 设计中的查找或创建习语?

Posted

技术标签:

【中文标题】REST API 设计中的查找或创建习语?【英文标题】:find-or-create idiom in REST API design? 【发布时间】:2014-04-15 22:44:15 【问题描述】:

假设我们有一个对“名称”有唯一约束的“用户”资源。您将如何设计 REST API 来处理查找或创建(按名称)用例?我看到以下选项:

选项 1:多个请求

客户:

POST /user
"name":"bob"

服务器:

HTTP 409 //or something else

客户:

GET /user?name=bob

服务器:

HTTP 200 //returns existing user

选项 2:一个请求,两个响应码

客户:

POST /user
"name":"bob"

服务器:

HTTP 200 //returns existing user

(如果实际创建了用户,则返回 HTTP 201)

选项 3:请求错误但响应数据包含冲突实体

客户:

POST /user
"name":"bob"

服务器:

HTTP 409 //as in option1, since no CREATE took place
"id": 1, "name":"bob" //existing user returned

【问题讨论】:

【参考方案1】:

我会选择选项 2,原因有两个:

首先,HTTP 响应代码,2xx(例如 200 nd 201)是指与 4xx 不同的成功操作。因此,在 find 或 create 发生这两种情况下,您的操作都会成功。

其次,选项 1 使对服务器的请求数量增加一倍,这可能会导致负载过重时的性能下降。

【讨论】:

该选项确实不那么健谈。但是,如果我们有另一个用例——用户创建怎么办。在这种情况下,我们不希望在尝试创建新记录时静默返回现有记录。如果我们在这里选择选项 2,这是否意味着需要另一个端点(这不会违反 REST 的约定吗?);还是客户端必须将 HTTP 200(vs 201)解释为注册用户案例中的错误? @Nikita 如果您选择选项 2,那么是的,客户端将需要查看响应状态以确定用户是否已创建。【参考方案2】:

我相信“正确”的 RESTful 方式是:

GET /user?name=bob
   200: entity contains user
   404: entity does not exist, so
        POST /user  "name" : "bob" 
           303: GET /user?name=bob
                200: entity contains user

我也是 Post-Redirect-Get 模式的忠实拥护者,这需要服务器使用新创建用户的 uri 向客户端发送重定向。然后,您在 POST 案例中的响应将在其正文中包含状态代码为 200 的实体。

这确实意味着到服务器的 1 或 3 次往返。 PRG 的一大优势是在页面重新加载时保护客户端免于重新发布,但您应该阅读有关它的更多信息以确定它是否适合您。

如果这与服务器的来回切换太多,您可以执行选项 2。根据我对 https://www.rfc-editor.org/rfc/rfc2616#section-9.5 的阅读,这不是严格的 RESTful:

POST 方法执行的操作可能不会产生资源 可以通过 URI 标识。在这种情况下,200(OK)或 204 (No Content) 是适当的响应状态,取决于是否 响应中是否包含描述结果的实体。

如果您可以偏离标准,并且担心往返,那么选项 2 是合理的。

【讨论】:

非常有帮助,@Eric Stein。澄清 - 在上述方法中,当另一个进程在初始 GET 之后但在 POST 之前创建“bob”时会发生什么?即在这种情况下对 POST 的响应是什么 - 200 与(现在)现有用户在正文中? @Nikita 在这种情况下,我希望 POST 返回 403。根据规范,409 不好:“此代码仅在预期用户可能是能够解决冲突并重新提交请求。”请注意,在这种情况下,关于正确的响应代码存在很多争论,因此您应该 Google 以更详细地了解该问题。【参考方案3】:

我正在使用 选项 2 的版本。创建资源时返回 201,仅检索资源时返回 303 ("see other")。我之所以选择这样做,部分原因是 get_or_create 似乎不是一个常见的 REST 习惯用法,而且 303 是一个稍微不寻常的响应代码。

【讨论】:

但是 POST 不是幂等的。 restfulapi.net/idempotent-rest-apis【参考方案4】:

我相信一个 GET 请求:

返回现有记录;或 创建一条新记录并返回它

是我在此处的回答中讨论的最有效的方法:Creating user record / profile for first time sign in

正如@Cormac Mulhall 和@Blake Mitchell 在REST Lazy Reference Create GET or POST? 中解释的那样,服务器需要在响应中返回记录之前创建记录是无关紧要的

这在Section 9.1.1 of the HTTP specification中也有解释:

当然,无法确保服务器不会因为执行 GET 请求而产生副作用;事实上,一些动态资源认为这是一个特性。 这里的重要区别是用户没有请求副作用,因此不能对它们负责。

【讨论】:

RFC2616 已过时。不要使用它。这是一个参考:mnot.net/blog/2014/06/07/rfc2616_is_dead

以上是关于REST API 设计中的查找或创建习语?的主要内容,如果未能解决你的问题,请参考以下文章

REST API 设计 - 通过 ID 加载创建资源

在 PayPal REST API 中查找 Braintree 计费协议 ID

Rest api 设计:使用重复数据创建 POST,可能是 IntegrityError/500,啥是正确的?

如何使用 REST API 创建 Azure 搜索索引器

使用操作挂钩创建自定义Wordpress REST API端点

通过Google Drive REST API上传或创建包含内容的新文件?