什么使用PATCH或POST?

时间:2019-06-03 22:10:02

标签: rest http state-machine

我和我的同事进行了长时间的静悄悄的辩论,讨论将用于更改资源状态的操作之一的正确HTTP动词。

假设我们有一个名为WakeUpLan的资源,该资源试图将事件发送到网络中连接的系统。这是一种通用状态机,

{
id: 1,
retries: {
 idle: 5, // after 5 retries it went to FAILED state
 wakeup: 0,
 process: 0,
 shutdown: 0
},
status: 'FAILED',
// other attributes
}`

IDLE --> WAKEUP ---> PROCESS ---> SHUTDOWN | ----> [FAILED]

每个状态都有重试机制,例如,在IDLE情况下,它尝试xtransitionWAKEUP的次数,而在x重试之后,它就消失了并进入FAILED状态。

可以再次手动重新启动所有FAILED资源,或从某个界面重试一次。

因此,对于哪种HTTP动词最适合这种情况,我们感到困惑。

在我看来,这只是status中的一项更改,并将重试计数重置为0,以便我们的重试机制可以捕获该错误并在下一次迭代中进行尝试。 因此它应该是纯PATCH请求

PATCH retry/{id} {state: 'IDLE'}

但是我的同事反对将其作为POST请求,因为这是纯粹的操作,应将其视为POST。 我不相信,因为我们不是在创建任何新资源,而是在更新我们的REST服务器已经知道的现有资源。

如果我错了,我想知道并纠正。

欢迎任何建议。

谢谢。

3 个答案:

答案 0 :(得分:1)

HTTP基础

PATCH

PATCH实际上是什么? PATCHRFC 5789中定义的HTTP方法,类似于软件工程中的修补代码,在该方法中,应应用对一个或多个源的更改,以将目标资源转换为所需的结果。因此,客户正在计算一组指令,目标系统必须完全应用这些指令才能生成请求的结果。这些指令通常称为“补丁”,用RFC 5789的话来说,这样的指令集称为“补丁文档”。

RFC 5789没有定义这种补丁文件需要以哪种表示形式从一个系统转移到另一个系统。对于基于JSON的表示,可以使用application/json-patch+json (RFC 6902),其中包含或多或少的某些指令,例如addreplacemovecopy,...清楚他们在做什么,但是RFC还进一步描述了每个可用的说明。

application/merge-patch+json (RFC 7386)中捕获了关于如何通知系统如何更改资源(或文档)的另一种基于JSON的方法,但完全不同。与json-patch相比,此media-type确实定义了一组默认规则,这些规则适用于接收到基于JSON的实际目标资源表示。在这里,将已修改状态的单个JSON表示发送到服务器,该服务器仅包含应由服务器更改的字段和对象。默认规则定义要从目标资源中删除的字段需要在请求中为空,而应更改的字段需要包含要应用的新值。可以在请求中忽略保留不变的字段。

如果您阅读RFC 5789,则会发现merge-patch更像是一种黑客。与json-patch相比,merge-patch表示缺乏对应用指令的实际顺序的控制,这不一定总是必要的,并且缺少一次更改多个不同资源的机会。

PATCH本身不是幂等的。对于json-patch补丁文件,很明显,多次应用相同的指令可能会导致不同的结果,即,如果删除了第一个字段。这里的merge-patch文档的行为类似于“部分PUT”的请求,由于实用主义,尽管实际操作仍然不能保证幂等,但许多开发人员还是出于实用主义的要求而执行。为了避免无意多次将同一补丁应用于同一资源,即由于传输补丁文档时出现网络错误,建议在conditional requests (RFC 7232)旁使用PATCH。这样可以确保将更改仅应用于资源的特定版本,并且如果该资源是通过先前的请求或外部源进行了更改,则该请求将被拒绝以防止数据丢失。这基本上是乐观锁定。

所有补丁文件都必须满足的要求是,必须以原子方式应用它们。所有更改要么全部应用,要么根本不应用。这给服务提供商带来了一些交易负担。

POST

POST方法在RFC 7231中定义为:

  

请求目标资源根据资源自身的特定语义处理请求中包含的表示形式。

从根本上讲,这是一张免费的监狱卡,可让您在此处执行您想做的或必须做的任何事情。您可以自由定义要在特定端点上接收的语法和结构。大多数这些所谓的“ REST API”都将POST视为CRUD中的C,可以将POST用作C,但实际上只是过分简化了它对您的作用。 HTML基本上仅支持POSTGET操作,因此POST请求用于将各种数据发送到服务器以启动支持流程,创建新的资源,例如博客文章,问答。 ,视频……,还可以删除或更新内容。

这里的经验法则是,如果由于在某个URI上触发POST请求而创建了新资源,则响应代码应为201 Created,其中包含HTTP响应标头Location,其中包含URI作为指向新创建的资源的值。在任何其他情况下,POST都不会映射到CRUD构造型的C(创建)。

与REST相关

REST不是协议而是建筑风格。正如Robert(Bob叔叔)C. Martin stated所言,架构与意图有关,而REST意图与使客户端与服务器脱钩有关,这通过最小化因更改而引起的互操作性问题而允许后者自由发展。由服务器引入。

如果您的系统在未来几十年内仍能正常工作,那么这些好处将非常有用。但是,不幸的是,这些好处很难获得。如Fieldings dissertation中所述,要从REST中受益,必须严格遵守上述限制,否则耦合将继续增加因更改而中断客户端的可能性。后来,人们对那些没有读过或不理解他的论文的人大喊大叫,并阐明了REST API必须做什么in a nutshell

这种咆哮可以归纳为以下几点:

  • API应该遵守并且不违反底层协议。 REST大部分时间都是通过HTTP使用的,但不仅限于此协议。
  • 强烈关注资源及其通过媒体类型的呈现。
  • 客户不应该对API中的可用资源或其返回状态("typed" resource)有初步的了解或假设,而应通过发出的请求和响应来动态学习它们,这些请求和响应可以告诉客户下一步该做什么。这样一来,服务器就可以自由使用其名称空间,并且可以在不影响客户端的情况下进行所需的处理。

基于此,REST是关于使用定义明确的标准并遵守用作运输工具的协议的语义。通过利用HATEOAS和无状态通信,这些概念证明了Web具有可伸缩性和发展友好性,现在REST体系结构中的应用程序使用了与人类在Web中使用的交互模型相同的交互模型。

公用媒体类型提供了affordance,说明系统可以如何处理针对该有效负载接收的数据,而内容类型协商则保证发送方和接收方都能够处理和理解有效负载正确地。承受能力可能因媒体类型而异。为image/png接收的有效负载可能会呈现并显示给用户,而application/vnd.acme-form+json可能会定义一种形式,在这种形式下,服务器会教客户有关服务器所支持的请求元素的信息,并且客户可以输入数据并发出请求,而不必主动知道使用方法或目标URI将数据发送到该方法,因为服务器已经给出了该方法。这不仅消除了对带外(外部)API文档的需求,而且消除了客户端解析或解释URI的需求,因为它们都是由服务器提供的,并附带link-relations,应该是standardized by IANA,遵循existing rel values microformatsontologies like Dublin Core之类的通用约定,或代表extension types as defined in RFC 5988 (Web linking)

与问题有关

在介绍完之后,我希望对于这样的问题

  

但是我的同事反对将其作为POST请求,因为这是纯粹的操作,应将其视为POST。我不相信,因为我们不是在创建任何新资源,而是在更新我们的REST服务器已经知道的现有资源

很明显,此任务没有明确的答案,但更多取决于它的

可能要问几个问题,例如

  • 有多少(不同)客户将使用该服务?它们全都在您的控制之下吗?如果是这样,则不需要REST,但仍然可以针对它
  • 如何教会或指导客户执行更新?您将提供外部API文档吗?您是否支持一种支持表单的媒体类型,例如HTMLhal-formshalo+jsonIonHydra

通常,如果您有多个客户端,尤其是不受您控制的客户端,则可能不知道它们支持哪些功能。这里,内容类型协商是重要的部分。如果客户端支持application/json-patch+json,则它也可能能够计算出包含适用于目标资源的说明的补丁文档。正如RFC 6902所提到的,它也将支持PATCH的可能性也很大。在这种情况下,提供客户端可以向其发送请求的PATCH端点将是有意义的。

如果客户端支持application/patch-merge+json,则可能会假定它也支持PATCH,因为它主要用于RFC 7386的HTTP PATCH方法。在这里,来自客户端的更新侧面的观点相当琐碎,因为更新后的文档按原样发送到服务器。

在任何其他情况下,都不清楚将更改以哪种表示形式传输到服务器。在这里,POST可能是可行的方法。从REST立场来看,这里的更新可能类似于对浏览器中在Web表单中编辑的数据所做的更新,并且当前内容已加载到每个表单元素中,并且客户端根据自己的喜好修改了这些表单元素然后以application/x-www-form-urlencoded(或类似结构)将更改提交回服务器。不过,在这种情况下,PUT可能更合适,因为在这种情况下,您会将资源的整个更新状态传送回服务,因此对目标执行完全更新而不是部分更新资源。表单将提交的实际媒体类型可能在相应表单的媒体类型中定义。请注意,这并不意味着您也无法处理json-patch中的merge-patchPOST文档。

这里的经验法则是,您支持的媒体类型格式和HTTP方法越多,不同的客户端就越有可能实际执行其任务。

答案 1 :(得分:1)

  

欢迎任何建议。

REST architectural style的参考实现是万维网。万维网建立在URI,HTTP和HTML的基础上,并且HTML表单处理仅限于GETPOST

因此POST必须是一个可接受的答案。毕竟,网络是灾难性的成功。

PATCH一样,

PUT允许您传达对资源表示形式的更改。语义比POST更具体,这使客户端可以更好地利用。因此,如果您要做的只是创建一条消息,描述对资源表示形式的本地编辑,那么PATCH是一个不错的选择。

不要忽略PUT的可能性-如果资源的完整表示的大小与PATCH文档的表示的大小大致相同,则使用PUT可能是更好的选择,因为{ {3}}。

  

我不相信,因为我们不是在创建任何新资源,而是在更新我们的REST服务器已经知道的现有资源。

idempotent semantics比“创建新资源”更为笼统。从历史上看,围绕这一点有很多困惑(早期HTTP规范中的语言没有帮助)。

答案 2 :(得分:-3)

我想说您是对的,因为您没有创建任何新资源。 突出显示在修改整个现有资源时使用放置的部分,在修改现有资源的一个组件时使用使用补丁的部分。 这里更多 https://restfulapi.net/rest-put-vs-post/