将 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/<id>/<validFrom>
我们更改日期以上是关于将 POST 用作 UPDATE 或 CREATE 时是不是违反了 RESTful的主要内容,如果未能解决你的问题,请参考以下文章
Django“update_or_create”API:如何通过创建或更新过滤对象?