将 POST 用作 UPDATE 或 CREATE 时是不是违反了 RESTful

Posted

技术标签:

【中文标题】将 POST 用作 UPDATE 或 CREATE 时是不是违反了 RESTful【英文标题】:Do I violate RESTfulness when using POST as UPDATE OR CREATE将 POST 用作 UPDATE 或 CREATE 时是否违反了 RESTful 【发布时间】:2014-09-18 09:33:56 【问题描述】:

鉴于其他部门对我们的 REST API 的要求,他们希望将 POST 不仅用于 CREATE,还用于 UPDATE OR CREATE。我知道在 RESTful API 中 PUT 可以或应该用于此目的,但由于客户端必须更新用于构建 URI 的信息,我们不能使用它。它将更改 URI 并使 PUT 不再具有幂等性...(在第一个 PUT 之后旧的 URI 将不存在)。tl;dr 我们不能使用 PUT

在HTTP/1.1 specs POST 中定义为

POST 方法用于请求源服务器接受 包含在请求中的实体作为资源的新下属 由 Request-URI 标识

还有

POST 方法执行的操作可能不会产生资源 可以通过 URI 识别。

为了保持 RESTful,我会将更新功能解释为删除旧元素,然后创建新元素,这对于 POST 来说是可以接受的功能。

我们会在创建成功时返回#201,在更新时返回#200


在我们的 API 中,“有可能”在没有 URI 的情况下处理正确的元素(例如,使用 POST 更新它),因为所有 URI 构建主键部分都在资源主体中,因此 API知道客户想要访问哪个元素。


示例

(这只是POST 的行为示例。不适用于资源的数据结构。当然使用PUT/cars/ 来说是完全正确的) p>

POST /cars/ HTTP/1.1
<car>
    <id>7</id>
    <status>broken</status>
</car>

回复:#201 然后

POST /cars/ HTTP/1.1
<car>
    <id>7</id>
    <status>fine</status>
</car>

回复:#200 现在/cars/7 上的GET 将返回以下内容:

<car>
    <id>7</id>
    <status>fine</status>
</car>

【问题讨论】:

在查看您的示例后,我刚刚删除了我的原始评论,因为您的事务似乎是幂等的。您正在将 7 号车的状态设置为正常。如果您一次又一次地重新运行此方法,那么您仍然会得到相同的结果。因此,没有充分的理由认为这个特定示例不能是 PUT 方法。 这个answer 涵盖了我感觉最好的 PUT v POST。如果您的操作是幂等的,则使用 PUT,否则使用 POST。 @ydaetskcoR 我认为您的第一条评论很好。该示例仅针对行为(将其添加到问题中)。抱歉含糊不清! :/ 我也看到了你引用的答案,但遗憾的是它并没有真正使用好的资源(PDF 并不是很有帮助)。 Btu 来评论您的最后一条语句 My POST (with Update or Create) 现在有点无能,因为一次又一次地调用它会产生相同的数据集。对于我的解决方案来说,用 PUT 创建这种幂等性似乎是不可能的(因为 uri 发生了变化) 【参考方案1】:

“违反 RESTfulness”没有严格定义。

degrees 接口中类似 REST 的行为的一个很好的参考是 Richardson Maturity Model,它将对 REST 理想的支持程度分为 4 层(其中 0 表示“根本不是 REST”,并且3 是“完全 REST,但几乎没有人这样做”)

在此模型的第 2 级基于 RESTful HTTP 的设计中,您的选择仅略有不同。然而,这是许多现实世界项目必须应对的妥协。

POST 是开放式的,如果您愿意,它可以是幂等的,但 HTTP 不需要它。所以你并没有破坏 HTTP,只是错过了使用更密切的 PUT 方法的机会。相反的情况,将 PUT 用于 API 的非幂等部分,问题会更大。

作为个人观点,我认为如果您在处理不同的 HTTP 方法和路由时保持自洽,并清楚地记录这种预期的变化,那么您可以合理地将您的 API 称为“RESTful”。我认为响应代码拆分 200/201 足以编写一个自洽的客户端。虽然我更希望在这里看到 create 和 update 都作为 PUT,但这不会让我停顿片刻。

您可能会考虑按照建议在收集路线上支持 update 或 create(在您的示例中为 POST /cars),并在项目路线上支持 update (POST /cars/7) . “更新或创建”的概念在数据库和 ORM 框架中很常见,很容易理解。客户端也可以使用后一种仅更新路由,因为它不会意外地自动创建新记录。

【讨论】:

我已经阅读了您的链接,并认为它对我解释 API 开发文档中与标准的偏差非常有用。虽然我知道很难定义“宁静”,但您的想法和解释涵盖了我的思路。非常感谢您的回答! (如果其他人有新内容要添加,我会等待,然后将您标记为答案:)) “偏离标准” - REST 不是标准。或者如果是,它的参考编号是多少? :) 这是一种架构模式。你选择你跟随它的程度。您还可以选择您的实体建模。遵循 CRUD 不是强制性的。 是的。但为了获得最佳实践,请将 POST 作为创建和 PUT 作为更新分开。 @hien711:情况并非总是如此。当 id 已知且有意义时,PUT 对创建很有意义。如果不清楚,也许可以单独问一个问题。【参考方案2】:

自然不能保证服务器不 由于执行 GET 请求而产生副作用;在 事实上,一些动态资源认为这是一个特性。重要的 这里的区别是用户没有请求副作用,所以 因此不能对他们负责。

方法也可以具有“幂等性”的属性(除了 来自错误或过期问题)N > 0 的副作用相同 requests 与单个请求相同。

所以幂等性是关于服务器端的副作用。你的第二个 PUT 没有任何其他副作用(因为 404),而不是第一个,所以在这里使用 PUT 是幂等的。

POST 方法用于请求源服务器接受 包含在请求中的实体作为资源的新下属 由 Request-URI 标识

在这种情况下,您无需创建新资源,只需添加新资源标识符并删除旧资源标识符。所以POST是不合适的。

POST 方法执行的操作可能不会产生资源 可以通过 URI 识别。

这可能意味着你想要的任何东西......当他们没有选择时,Ppl 将 POST 用于非创作目的,但你肯定不是。

在我们的 API 中,“有可能”在没有 URI(例如,使用 POST 更新它),因为所有 URI 都在构建 主键部分在资源体中,所以 API 知道哪个 客户想要访问的元素。

如果您不想违反统一接口(资源识别)约束,则必须使用 IRI (URL) 来识别资源。

如果您不想违反统一接口(超媒体作为应用程序状态的引擎)约束,则必须使用超链接。 IRI 结构不为客户端携带任何语义,所以它可以是任何东西,如果由它们决定的话。您必须使用链接关系或词汇中的某些属性来添加语义。

如果您不想违反统一接口(自描述消息)约束,则必须至少使用供应商特定的 MIME 类型。

根据定义,REST web 服务满足所有REST constraints。术语 RESTful 是在人们开始将他们的 Web 服务称为 REST 时创建的——他们对 REST 毫无概念。因此,在那之后,人们创建了 RESTful 术语,这意味着所谓的 REST 服务不违反任何 REST 约束。在那之后,RESTful 一词也用尽了。现在我们区分超媒体 API 和 Web API。超媒体 API 意味着 Web 服务符合 HATEOAS(超媒体作为应用程序状态引擎)约束。 Web API 可以是任何声称是 REST 的东西。

【讨论】:

如果我接受 PUT 来更新这些实体,我必须接受 URI 与正文不同的请求,这对我来说感觉不好,但也许这是没有根据的。但是我的 API 应该如何对第二个 PUT 做出反应? #404 是不正确的,因为它是合法的调用。客户端想要更新或创建资源,API 不能否认,因为没有具有该 URI 的元素。 PUT 调用必须创建资源,如果它不存在的话。但是然后...... URI 对于任何 PUT 调用都变得无关紧要,因为无论如何我都会忽略它并使用身体。这真的是hypermedia API!? 非常感谢您冗长而详细的回答!我对幂等的理解是错误的。如果您能将我上次评论中的担忧纳入您的回答中,那就太好了。 "If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI." - 如果您不想使用 PUT 创建新资源... can create != must create If I accept PUT for updating these entitys, I have to accept requests where the URI is different from the body, which feels not good for me, but maybe thats unfounded. 你能举个例子吗? 嗯,是的。 ***.com/a/25642689/2630337 几天前我们已经讨论过......对我来说,问题是 API 应该如何表现?忽略 URI 并从正文中对给定资源进行更新/创建?或者只是查看 URI 下是否有资源并尝试更新它?但是,如果在给定的 URI 下没有资源怎么办:使用正文中给出的主键或 URI 中给出的主键创建一个新资源?这对我来说感觉很不一致... |我们正在使用的真实实体类似于:/parameter/&lt;id&gt;/&lt;validFrom&gt; 我们更改日期

以上是关于将 POST 用作 UPDATE 或 CREATE 时是不是违反了 RESTful的主要内容,如果未能解决你的问题,请参考以下文章

将变量传递给注入的服务以用作装饰器参数

Django“update_or_create”API:如何通过创建或更新过滤对象?

如何从 SQL 查询中的 CREATE/UPDATE/INSERT 语句中提取表名?

Restful接口规范

Django 批处理/批量更新或创建?

如何在ActiveRecord的关系表中使用枚举?