HTTP中的POST和PUT有什么区别?

http rest post put

根据 RFC 2616, § 9.5POST 用于创建一个资源:

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

根据 RFC 2616, § 9.6PUT 用于创建或替换资源:

PUT 方法请求将封闭的实体存储在提供的 Request-URI 下。如果 Request-URI 引用了一个已经存在的资源,封闭的实体应该被认为是在源服务器上的一个修改版本。如果 Request-URI 不指向现有资源,并且该 URI 能够被请求用户代理定义为新资源,则源服务器可以使用该 URI 创建资源。

那么应该使用哪种 HTTP 方法来创建资源呢?还是应该同时支持?

使用 HTTPbis 中的定义可能会有所帮助 - Roy 投入了大量工作来澄清它们。请参阅:tools.ietf.org/html/…

只是为了将 @MarkNottingham 的评论带到最新版本,这里是 HTTPbis 上定义的 POSTPUT

在我看来,这场争论源于通过根据 CRUD 操作描述 HTTP 方法来过度简化 REST 的常见做法。

不幸的是,关于 POST 的第一个答案是错误的。检查我的答案以获得对差异的更好解释:stackoverflow.com/a/18243587/2458234

PUT 和 POST 都是不安全的方法。但是,PUT 是幂等的,而 POST 不是。 - 查看更多信息:restcookbook.com/HTTP%20Methods/put-vs-post/…

C
Community

全面的:

PUT 和 POST 都可用于创建。

你必须问,“你在做什么?”,以区分你应该使用什么。假设您正在设计一个用于提问的 API。如果您想使用 POST,那么您可以对问题列表执行此操作。如果您想使用 PUT,那么您将对特定问题执行此操作。

太好了,两者都可以使用,所以我应该在我的 RESTful 设计中使用哪一个:

您不需要同时支持 PUT 和 POST。

您使用哪个取决于您。但是请记住根据您在请求中引用的对象来使用正确的对象。

一些考虑:

您是明确命名您创建的 URL 对象,还是让服务器决定?如果您命名它们,则使用 PUT。如果您让服务器决定然后使用 POST。

PUT 被定义为假定幂等性,所以如果你 PUT 一个对象两次,它应该没有额外的效果。这是一个不错的属性,所以我会尽可能使用 PUT。只需确保 PUT 幂等性实际上在服务器中正确实现。

您可以使用具有相同对象 URL 的 PUT 更新或创建资源

使用 POST,您可以同时收到 2 个请求来修改 URL,它们可能会更新对象的不同部分。

一个例子:

我在 another answer on SO regarding this 中写了以下内容:

POST:用于修改和更新资源 POST /questions/ HTTP/1.1 主机:www.example.com/ 注意以下是错误:POST /questions/ HTTP/1.1 主机:www.example .com/ 如果尚未创建 URL,则在指定名称时不应使用 POST 创建它。这应该会导致“找不到资源”错误,因为 尚不存在。您应该首先将 资源放在服务器上。您可以使用 POST 来创建资源: POST /questions HTTP/1.1 Host: www.example.com/ 请注意,在这种情况下,未指定资源名称,新的对象 URL 路径将返回给您. PUT:用于创建资源,或覆盖它。在您指定资源的新 URL 时。对于新资源:PUT /questions/ HTTP/1.1 主机:www.example.com/ 覆盖现有资源:PUT /questions/ HTTP/1.1 主机:www.example.com/

此外,更简洁一点的是,RFC 7231 Section 4.3.4 PUT 状态(添加了重点),

4.3.4. PUT PUT 方法请求创建目标资源的状态或将其替换为请求消息有效负载中包含的表示定义的状态。

我认为 PUT 是幂等的这一事实再怎么强调也不为过:如果网络出现问题并且客户端不确定他的请求是否通过,它可以发送第二次(或第 100 次),并且由HTTP 规范,这与发送一次具有完全相同的效果。

@Jörg W Mittag:没必要。如果请求同时被修改(由其他用户或第一个请求本身通过),第二次可能会返回 409 Conflict 或其他内容。

如果我没记错的话,我们应该强调的是 PUT 被定义为幂等的。您仍然必须以 PUT 行为正确的方式编写服务器,是吗?也许最好说“PUT 导致传输假定幂等性,这可能会影响传输的行为,例如缓存”。

@JörgWMittag 幂等标语? “发啊发啊发啊朋友啊,到底有没有区别”

将它们视为: PUT = 插入或更新; POST = 插入。因此,当您进行两次 PUT 时 - 您将获得一条新记录,当您进行两次 POST 时 - 您将获得两条新记录。

t
the Tin Man

你可以在网上找到断言说

POST用于创建资源,PUT用于修改资源

PUT 用于创建资源,POST 用于修改资源

两者都不完全正确。

更好的是根据动作的 idempotence 在 PUT 和 POST 之间进行选择。

PUT 意味着放置一个资源 - 用不同的东西完全替换给定 URL 上可用的任何东西。根据定义,PUT 是幂等的。你喜欢做多少次,结果都是一样的。 x=5 是幂等的。您可以放置一个资源,无论它以前是否存在(例如,创建或更新)!

POST 更新资源、添加辅助资源或导致更改。 POST 不是幂等的,就像 x++ 不是幂等的一样。

通过这个论点,PUT 用于在您知道要创建的事物的 URL 时进行创建。当您知道要创建的事物类别的“工厂”或经理的 URL 时,可以使用 POST 来创建。

所以:

POST /expense-report

或者:

PUT  /expense-report/10929

我同意,只要涉及幂等性,它就应该胜过任何其他问题,因为错误会导致许多意想不到的错误。

如果POST可以更新资源,那怎么不是幂等的?如果我使用 PUT 更改学生的年龄并执行 10 次,那么如果我执行一次,则学生的年龄相同。

@Schneider,在这种情况下,您的服务器正在付出额外的努力来保证幂等性,但它并没有宣传它。如果他们尝试重新加载这样的 POST 请求,浏览器仍然会警告用户。

@Schneider POST 可能会创建一个附属资源;因此,您可以 POST 到集合,例如 POST /expense-reports,它会在您的服务器上创建与您发送的请求数量一样多的实体(费用报告),即使它们完全相似。将其视为使用自动递增主键在数据库表 (/expense-reports) 中插入同一行。数据保持不变,密钥(在这种情况下为 URI)由服务器生成,并且对于每个其他插入(请求)都不同。所以,POST 效果可以是幂等的,但也可能不是。因此,POST 不是幂等的。

假设我们有可能具有两个属性的实体 - namedate。如果我们有一个具有现有 namedate 的实体,但随后仅指定 name 向它发出请求,则 PUT 的正确行为将是删除 date实体,而 POST 可能只更新指定的属性,将未指定的属性保留为发出请求之前的状态。这听起来正确/合理,还是对 PUT 的不当使用(我看到了对 PATCH 的引用,这似乎更合适,但还不存在)?

C
Community

POST 到 URL 在服务器定义的 URL 处创建子资源。

PUT to a URL 在客户端定义的 URL 处创建/替换整个资源。

对 URL 的 PATCH 会更新该客户端定义的 URL 处的部分资源。

PUT 和 POST 的相关规范是 RFC 2616 §9.5ff.

POST 创建一个子资源,因此 POST 到 /items 创建一个位于 /items 资源下的资源。例如。 /items/1。两次发送相同的 post 数据包将创建两个资源。

PUT 用于在客户端已知的 URL 上创建或替换资源。

因此: PUT 只是 CREATE 的候选者,其中客户端在创建资源之前已经知道 url。例如。 /blogs/nigel/entry/when_to_use_post_vs_put 作为标题用作资源键

如果已知 url 处的资源已经存在,则 PUT 会替换该资源,因此两次发送相同的请求无效。换句话说,对 PUT 的调用是幂等的。

RFC 是这样写的:

POST 和 PUT 请求的根本区别体现在 Request-URI 的不同含义上。 POST 请求中的 URI 标识将处理封闭实体的资源。该资源可能是一个数据接受进程,一个通往其他协议的网关,或者一个接受注释的单独实体。相比之下,PUT 请求中的 URI 标识了请求中包含的实体——用户代理知道 URI 的意图,服务器不得尝试将请求应用于其他资源。如果服务器希望将请求应用于不同的 URI,

注意: PUT 主要用于更新资源(通过整体替换它们),但最近出现了使用 PATCH 更新现有资源的趋势,因为 PUT 指定它替换整个资源。 RFC 5789.

2018 年更新:有一种情况可以避免 PUT。请参阅"REST without PUT"

使用“没有 PUT 的 REST”技术,其想法是强制消费者发布新的“统一”请求资源。如前所述,更改客户的邮寄地址是对新“ChangeOfAddress”资源的 POST,而不是具有不同邮寄地址字段值的“客户”资源的 PUT。

取自 REST API Design - Resource Modeling by Prakash Subramaniam of Thoughtworks

这迫使 API 避免多个客户端更新单个资源的状态转换问题,并与事件溯源和 CQRS 更好地匹配。当工作异步完成时,发布转换并等待它被应用似乎是合适的。

或者从围栏的另一边:如果客户端确定了结果资源的地址,则 PUT,如果服务器确定则 POST。

我认为应该编辑这个答案,以便更清楚@DanMan 以一种非常简单的方式指出的内容。我发现这里最有价值的是最后的注释,指出 PUT 应该仅用于替换整个资源。

PATCH 至少在几年内都不是一个现实的选择,但我同意这种意识形态。

我试图理解,但使用 PUT 创建某些东西只有在客户确定资源不存在的情况下才有意义,对吧?以博客为例,假设您在几年内创建了数百篇博客文章,然后意外选择了与两年前的文章标题相同的标题。现在你已经去替换了那个帖子,这不是故意的。因此,使用 PUT 进行创建将需要客户端跟踪哪些内容被占用,哪些未被占用,并且可能导致事故和意想不到的副作用,以及拥有做两件完全不同的事情的路线?

你是对的。将博客帖子放在与现有帖子相同的 url 会导致对该现有帖子的更新(尽管您显然可以先使用 GET 进行检查)。这说明了为什么只使用标题作为 URL 是个坏主意。但是,它可以在数据中有自然键的任何地方工作……根据我的经验,这种情况很少见。或者,如果您使用 GUID

A
Ashwini Chougale

POST 表示“创建新”,如“这是创建用户的输入,为我创建”。

PUT 表示“插入,如果已存在则替换”,如“这是用户 5 的数据”。

POST 到 example.com/users,因为您还不知道用户的 URL,您希望服务器创建它。

PUT访问 example.com/users/id,因为您想替换/创建一个特定用户。

使用相同的数据发布两次意味着创建两个具有不同 ID 的相同用户。使用相同的数据进行两次 PUT 会创建第一个用户,第二次将他更新到相同的状态(无更改)。由于无论执行多少次 PUT 之后都会以相同的状态结束,因此它被称为每次都“同等有效” - 幂等。这对于自动重试请求很有用。当您按下浏览器上的后退按钮时,不再有“您确定要重新发送”。

当您需要服务器控制资源的 URL 生成时,一般建议是使用 POST。否则使用 PUT。首选 PUT 而不是 POST

粗心可能导致人们普遍认为您只需要两个动词:GET 和 POST。 GET 获取,POST 更改。甚至 PUT 和 DELETE 也是使用 POST 执行的。 25 年后问 PUT 的真正含义可能表明我们一开始就理解错了。 REST 的流行驱使人们回到基础,我们现在必须忘记过去的错误。 POST 被过度使用,现在通常被错误地教授。最好的部分:“使用相同的数据发布两次意味着创建两个相同的 [资源]”。好点!

如果 ID 尚不存在,您如何使用 PUT 创建记录,例如在您的示例 user 5 中?你不是说update, replace if already exists吗?或者其他的东西

“更喜欢 PUT 而不是 POST”... 愿意证明这一点吗?

@thecoshman:当然。我写了这个作为一般建议。我的理由是 PUT 是幂等的,因此从网络的角度来看更好。 POST 也更通用,因此通过推荐 PUT,您可以避免将 POST 用于 PUT 就足够的情况。由于浏览器的限制,POST 也被过度使用,因此反对它的建议将对 REST 作为一个概念产生积极影响。当客户端控制 IMO 的 URL 构建时,URL 方案也有一些积极的影响,但我无法将其放入此处的评论中。

我会说使用相同数据发布两次可能会导致两个相同的用户。如果我创建我的 API,如果有人尝试使用相同的电子邮件地址发布新用户,但数据不同,我可能会发出 409。如果有人尝试使用相同的数据发布新用户,我可能会发出 303。我可能不希望我的系统能够拥有两个相同的用户。

S
Stephen Ostermiller

概括:

创造:

可以通过以下方式使用 PUT 或 POST 执行:

PUT 在 /resources URI 或集合下以 newResourceId 作为标识符创建新资源。 PUT /resources/ HTTP/1.1 POST 在 /resources URI 或集合下创建新资源。通常标识符由服务器返回。发布/资源 HTTP/1.1

更新:

只能通过以下方式使用 PUT 执行:

PUT 在 /resources URI 或集合下以 existingResourceId 作为标识符更新资源。 PUT /resources/ HTTP/1.1

解释:

当一般处理 REST 和 URI 时,左侧是通用的,右侧是特定的。泛型通常称为集合,更具体的项目可以称为资源。请注意,资源可以包含集合。

示例:<-- 通用 -- 特定 --> URI:website.example/users/john website.example - 整个站点用户 - 用户集合 john - 集合的项目,或资源 URI:website.example/users/ john/posts/23 website.example - 整个站点用户 - 用户集合 john - 集合的项目或资源帖子 - 来自 john 的帖子集合 23 - 来自 john 的帖子,标识符为 23,也是一个资源

当你使用 POST 时,你总是在引用一个集合,所以每当你说:

POST /users HTTP/1.1

您正在向用户集合发布新用户。

如果你继续尝试这样的事情:

POST /users/john HTTP/1.1

它会起作用,但从语义上讲,您是说要在 users 集合下的 john 集合中添加资源。

使用 PUT 后,您指的是资源或单个项目,可能在集合中。所以当你说:

PUT /users/john HTTP/1.1

您正在告诉服务器更新,或者如果它不存在,则创建用户集合下的 john 资源。

规格:

让我强调一下规范的一些重要部分:

邮政

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

因此,在集合上创建一个新资源。

PUT 方法请求将封闭的实体存储在提供的 Request-URI 下。如果 Request-URI 引用了一个已经存在的资源,封闭的实体应该被认为是在源服务器上的一个修改版本。如果 Request-URI 不指向现有资源,并且该 URI 能够被请求用户代理定义为新资源,则源服务器可以使用该 URI 创建资源。”

因此,根据资源的存在创建或更新。

参考:

HTTP/1.1 规范

维基百科 - REST

统一资源标识符 (URI):通用语法和语义

这篇文章有助于我理解 POST 将“某物”作为子项添加到给定集合 (URI),而 PUT 明确定义了给定 URI 位置的“某物”。

不,PUT 不用于更新或创建。是为了更换。请注意,您可以用某些东西代替任何东西以获得创建的效果。

@7hi4g0 PUT 用于完全替换更新,换句话说,它替换。你用什么东西代替什么,或者用一个全新的东西代替什么。 PUT 不是为了进行微小的更改(除非您让客户进行微小的更改并提供整个新版本,即使是保持不变的版本)。对于部分修改,PATCH 是首选方法。

@thecoshman您可以,但是其中还涵盖了 create 并不太清楚。在这种情况下,最好是明确的。

当然,您可以发布“更新”。如果您保留以前的版本(并且您可能想要这样做的原因有很多),那么您的更新不是幂等的,因此不能用 PUT 表示。 (或者换句话说,当你足够用力地盯着它时,一切都会变成一个集合)

S
Stephen Ostermiller

我想补充一下我的“务实”建议。当您知道可以检索所保存对象的“id”时,请使用 PUT。如果您需要返回一个数据库生成的 id 以供您将来查找或更新,则使用 PUT 将无法很好地工作。

所以:要保存现有用户,或者客户端生成 id 并且已验证 id 是唯一的用户:

PUT /user/12345 HTTP/1.1  <-- create the user providing the id 12345
Host: mydomain.example

GET /user/12345 HTTP/1.1  <-- return that user
Host: mydomain.example

否则,使用 POST 初始创建对象,并使用 PUT 更新对象:

POST /user HTTP/1.1   <--- create the user, server returns 12345
Host: mydomain.example

PUT /user/12345 HTTP/1.1  <--- update the user
Host: mydomain.example

实际上,它应该是POST /users。 (请注意,/users 是复数。)这具有创建新用户并使其成为 /users 集合的子资源的效果。

@DavidRR 公平地说,如何处理团体完全是另一场辩论。 GET /users 是有道理的,它可以按照您的意愿读取,但我可以使用 GET /user/<id>POST /user(带有所述新用户的有效负载),因为它正确读取 'get me users 5' 很奇怪,但是 'get me 用户 5' 更自然。不过,我可能仍然会支持多元化:)

@thecoshman 您可以像“从用户那里得到我的 id 5”一样阅读它;)

@xuiqzy 嗯,我真的很喜欢这种思考方式,并且很好地扩展 GET /users/5/documents/4/title 就像'获取用户,从那里获取用户 5,从那里获取文档,从那里获取文档 4,从那里给我标题'

P
Premraj

两者都用于客户端到服务器之间的数据传输,但它们之间存在细微差别,分别是:

PUT POST 替换现有资源或在资源不存在时创建。 www.example.com/com/customer/{customerId} www.example.com/com/customer/123/order/{orderId} 标识符由客户选择。创建新资源和从属资源,例如,文件从属于包含它的目录,或者行从属于数据库表。 www.example.com/com/customer/ www.example.com/com/customer/123/order/ 标识符由服务器返回 Idempotent 即如果您两次 PUT 资源,则它没有效果。示例:尽可能多地做它,结果将是相同的。 x=1; POST 既不安全也不幂等。示例:x++;以特定方式工作 以抽象方式工作 如果您使用 PUT 创建或更新资源,然后再次进行相同的调用,则该资源仍然存在并且仍然具有与第一次调用相同的状态。发出两个相同的 POST 请求很可能会导致两个资源包含相同的信息。

比喻:

PUT 即取放原处。

POST 作为在邮局发送邮件。

https://i.stack.imgur.com/AQKrz.jpg

社交媒体/网络类比:

在社交媒体上发布:当我们发布消息时,它会创建新帖子。

为我们已经发布的消息放置(即编辑)。

@MobileMon 不,REST 方法不是 CRUD。

我会说 PUT for UPSERTS

@MobileMon no :当您创建新资源并且您不知道获取它的最终端点时发布。 PUT 用于其他情况。

P
Peter Mortensen

使用 POST 创建,使用 PUT 更新。无论如何,Ruby on Rails 就是这样做的。

PUT    /items/1      #=> update
POST   /items        #=> create

POST /items 将新项目添加到已定义的资源(“项目”)。正如答案所说,它不会“创建一个组”。我不明白为什么这有 12 票。

开箱即用,Rails 不支持通过 REST“创建组”。要“创建一个组”,我的意思是“创建一个资源”,您必须通过源代码来完成。

这是一个公平的指导方针,但过于简单化了。正如其他答案所提到的,任何一种方法都可以用于创建和更新。

我同意这个答案,稍作修改。使用 POST 创建并使用 PUT 完全更新资源。对于部分更新,我们可以使用 PUT 或 PATCH。假设我们要更新组的状态。我们可以使用 PUT /groups/1/status 状态是请求负载或 PATCH /groups/1 使用负载中有关操作的详细信息

还应该明确的是,PUT /items/42 对于创建资源也是有效的,但前提是客户端有权命名资源。 (Rails 是否允许客户端这种命名特权?)

C
Community

REST 是一个非常高级的概念。事实上,它甚至根本没有提到 HTTP!

如果您对如何在 HTTP 中实现 REST 有任何疑问,可以随时查看 Atom Publication Protocol (AtomPub) 规范。 AtomPub 是一种使用 HTTP 编写 RESTful Web 服务的标准,由许多 HTTP 和 REST 杰出人士开发,其中一些输入来自 REST 的发明者和 HTTP 本人的(共同)发明者 Roy Fielding。

事实上,您甚至可以直接使用 AtomPub。虽然它来自博客社区,但绝不限于博客:它是一种通用协议,用于通过 HTTP 与任意(嵌套)任意资源集合进行 RESTful 交互。如果您可以将您的应用程序表示为资源的嵌套集合,那么您可以只使用 AtomPub,而不必担心是使用 PUT 还是 POST,返回什么 HTTP 状态代码以及所有这些细节。

这就是 AtomPub 对资源创建的看法(第 9.2 节):

要将成员添加到集合中,客户端将 POST 请求发送到集合的 URI。

允许 PUT 创建资源并没有错。请注意,这意味着客户端提供了 URL。

允许 PUT 创建资源有一些非常错误的地方:客户端提供了 URL。那是服务员的工作!

@Joshcodes创建客户端ID并非总是服务器的工作。我越来越多地看到让客户端生成某种 UUID 作为资源 ID 的设计。这种设计特别适合增加规模。

@JustinOhms 我同意您关于客户端生成的 ID 的观点(旁注:自 2008 年左右以来我设计的所有系统都要求客户端将 ID 创建为 UUID/Guid)。这并不意味着客户端应该指定 URL。

是的,如果资源已经存在,请使用 PUT。但是,几乎在所有情况下,都应使用 POST 创建资源,并且客户端不应提供 URL。 Roy Fielding 同意此声明 FWIW:roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven

C
Community

使用 PUT 还是 POST 在具有 HTTP + REST API 的服务器上创建资源的决定取决于谁拥有 URL 结构。让客户端知道或参与定义 URL 结构是一种不必要的耦合,类似于 SOA 产生的不希望的耦合。转义类型的耦合是 REST 如此受欢迎的原因。因此,正确使用的方法是 POST。此规则也有例外,当客户希望保留对其部署的资源的位置结构的控制权时,就会出现例外情况。这种情况很少见,很可能意味着有其他问题。

此时有些人会争辩说,如果使用 RESTful-URL,客户端确实知道资源的 URL,因此 PUT 是可以接受的。毕竟,这就是为什么规范化、规范化、Ruby on Rails、Django URL 很重要的原因,看看 Twitter API……等等等等。这些人需要明白,没有 Restful-URL 这样的东西,而且 Roy Fielding 自己说:

REST API 不得定义固定的资源名称或层次结构(客户端和服务器的明显耦合)。服务器必须可以自由控制自己的命名空间。相反,允许服务器通过在媒体类型和链接关系中定义这些指令来指导客户端如何构建适当的 URI,例如在 HTML 表单和 URI 模板中完成。 [这里的失败意味着客户端由于带外信息而假设资源结构,例如特定于域的标准,它是面向数据的等价于 RPC 的功能耦合]。 http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven

RESTful-URL 的想法实际上违反了 REST,因为服务器负责 URL 结构,应该可以自由决定如何使用它以避免耦合。如果这让您感到困惑,请阅读关于 API 设计中自我发现的重要性。

使用 POST 创建资源需要考虑设计,因为 POST 不是幂等的。这意味着多次重复 POST 并不能保证每次都具有相同的行为。这会吓到人们在不应该使用 PUT 来创建资源时使用。他们知道这是错误的(POST 用于 CREATE),但他们还是这样做了,因为他们不知道如何解决这个问题。这种担忧体现在以下情况:

客户端向服务器发布一个新资源。服务器处理请求并发送响应。客户端永远不会收到响应。服务器不知道客户端没有收到响应。客户端没有资源的 URL(因此 PUT 不是一个选项)并重复 POST。 POST 不是幂等的,服务器...

第 6 步是人们通常对要做什么感到困惑的地方。但是,没有理由创建一个组合来解决这个问题。相反,可以按照 RFC 2616 中的指定使用 HTTP 并且服务器回复:

10.4.10 409 Conflict 由于与资源的当前状态冲突,请求无法完成。仅在预期用户可能能够解决冲突并重新提交请求的情况下才允许使用此代码。响应正文应该包含足够的信息让用户识别冲突的来源。理想情况下,响应实体将包含足够的信息供用户或用户代理解决问题;但是,这可能是不可能的,也不是必需的。响应 PUT 请求时最有可能发生冲突。例如,如果正在使用版本控制并且被 PUT 的实体包括对资源的更改,这些更改与早期(第三方)请求所做的更改相冲突,则服务器可能会使用 409 响应来指示它无法完成请求.在这种情况下,响应实体可能会以响应 Content-Type 定义的格式包含两个版本之间差异的列表。

回复状态码 409 Conflict 是正确的方法,因为:

对 ID 与系统中已有资源匹配的数据执行 POST 是“与资源的当前状态冲突”。

因为重要的部分是让客户端了解服务器拥有资源并采取适当的行动。这是“预期用户可能能够解决冲突并重新提交请求的情况”。

包含具有冲突 ID 的资源的 URL 和资源的适当先决条件的响应将为“用户或用户代理解决问题提供足够的信息”,这是 RFC 2616 的理想情况。

基于 RFC 7231 发布的更新以替换 2616

RFC 7231 旨在替换 2616,并在 Section 4.3.3 中描述了 POST 的以下可能响应

如果处理 POST 的结果等同于现有资源的表示,则源服务器可以通过在 Location 字段中发送带有现有资源标识符的 303(参见其他)响应来将用户代理重定向到该资源。这样做的好处是为用户代理提供资源标识符并通过更适合共享缓存的方法传输表示,但如果用户代理尚未缓存表示,则会以额外请求为代价。

如果重复 POST,现在可能很想简单地返回 303。然而,事实恰恰相反。仅当多个创建请求(创建不同的资源)返回相同的内容时,返回 303 才有意义。一个例子是客户端不需要每次都重新下载的“感谢您提交请求消息”。 RFC 7231 在第 4.2.2 节中仍然坚持 POST 不是幂等的,并继续坚持应该使用 POST 进行创建。

有关这方面的更多信息,请阅读此article

409 Conflict 响应是否适合尝试使用已经存在的用户名创建新帐户之类的代码?我一直在专门使用 409 来解决版本冲突,但是在阅读了您的答案后,我想知道它是否不应该用于任何“重复”请求。

@EricB。是的,在您描述的“由于与资源的当前状态冲突”的情况下,操作失败。此外,可以合理地期望用户可以解决冲突,并且消息体只需要通知用户用户名已经存在。

@Joshcodes 你能多谈谈冲突解决过程吗?在这种情况下,如果用户名已经存在,客户端是否应该提示最终用户输入不同的用户名?如果客户端实际上是在尝试使用 POST 来更改用户名怎么办? PUT 请求是否仍用于更新参数,而 POST 用于创建对象,无论是一次一个还是多个?谢谢。

@BFar2 如果用户名已经存在,那么客户端应该提示用户。要更改用户名,假设用户名是需要修改的已创建资源的一部分,将使用 PUT,因为您是正确的,POST 用于创建,始终和 PUT 用于更新。

@Zuko,在我看来,自动递增表 ID 在分布式环境中没有位置。除了存储空间之外,UUID 在各方面都非常出色。用于 ID 的 Int 是从 DB 存储成为今天更大的关注点时的遗留物。

m
metamatt

我喜欢来自 RFC 2616's definition of PUT 的这个建议:

POST 和 PUT 请求的根本区别体现在 Request-URI 的不同含义上。 POST 请求中的 URI 标识将处理封闭实体的资源。该资源可能是一个数据接受进程,一个通往其他协议的网关,或者一个接受注释的单独实体。相比之下,PUT 请求中的 URI 标识了请求中包含的实体——用户代理知道 URI 的意图,服务器不得尝试将请求应用于其他资源。

这与此处的其他建议相吻合,即 PUT 最好应用于已经有名称的资源,而 POST 适合在现有资源下创建新对象(并让服务器为其命名)。

我将这一点以及 PUT 的幂等性要求解释为:

POST 适合在集合下创建新对象(并且 create 不需要是幂等的)

PUT 有利于更新现有对象(并且更新需要是幂等的)

POST 也可用于对现有对象的非幂等更新(尤其是更改对象的一部分而不指定整个事物——如果您考虑一下,创建集合的新成员实际上是这种类型的特例更新,从集合的角度来看)

当且仅当您允许客户端命名资源时,PUT 也可用于创建。但是,由于 REST 客户端不应该对 URL 结构做出假设,因此这不符合预期的精神。

“POST 也可用于对现有对象的非幂等更新(尤其是在不指定整个对象的情况下更改对象的一部分”这就是 PATCH 的用途

C
Community

简而言之:

PUT 是幂等的,如果执行一次或多次相同的操作,则资源状态将相同。

POST 是非幂等的,与执行一次相比,如果多次执行操作,资源状态可能会变得不同。

类比数据库查询

PUT 可以想到类似“UPDATE STUDENT SET address = "abc" where id="123";

POST 你可以想像 "INSERT INTO STUDENT(name, address) VALUES ("abc", "xyzzz");

学生 ID 是自动生成的。

使用 PUT,如果多次或一次执行相同的查询,则 STUDENT 表状态保持不变。

在 POST 的情况下,如果多次执行相同的查询,则会在数据库中创建多个学生记录,并且每次执行“INSERT”查询时数据库状态都会更改。

注意:PUT 需要一个需要更新的资源位置(已经资源),而 POST 不需要。因此,直观上 POST 是为了创建新资源,而 PUT 是为了更新已经存在的资源。

有些人可能会提出可以使用 POST 执行更新。没有硬性规定哪一个用于更新或哪一个用于创建。这些都是惯例,直觉上我倾向于上述推理并遵循它。

对于 PUT 类似于 INSERT 或 UPDATE 查询

实际上 PUT 你可以想到类似于 "UPDATE STUDENT SET address = "abc" where id="123"; 将是 PATCH 的语句。 "UPDATE STUDENT SET address = "abc", name="newname" where id=" 123" 将是 PUT 的正确类比

Put 也可用于 INSERT。例如,如果您的服务器检测到您尝试多次上传同一个文件,这将使您的请求具有幂等性。 (没有完成新文件上传)。

H
Homer6

POST 就像将一封信发送到邮箱或将电子邮件发送到电子邮件队列。 PUT 就像你把一个物体放在一个小房间或架子上的一个地方(它有一个已知的地址)。

使用 POST,您将发布到 QUEUE 或 COLLECTION 的地址。使用 PUT,您将放入 ITEM 的地址。

PUT 是幂等的。您可以发送请求 100 次,这无关紧要。 POST 不是幂等的。如果您发送请求 100 次,您将在邮箱中收到 100 封电子邮件或 100 封信件。

一般规则:如果您知道项目的 id 或名称,请使用 PUT。如果您希望接收方分配项目的 id 或名称,请使用 POST。

https://i.stack.imgur.com/ugbjx.png

不,PUT 意味着您知道 URL。如果您只知道 ID,则使用该 ID 发布以获取 URL。

id 是 URL 的一部分,所以是的,如果您知道 URL(包括 id),请使用 PUT。

不,URL 由服务器确定,ID 不一定是 URL 的一部分。 Roy Fielding 会告诉您same,或者您可以直接阅读他的thesis

@Joshcodes,这是假设 REST 吗?在 RESTful 架构中,项目 ID 绝对是 URL 的一部分,例如:/people/123。我喜欢这个 REST 站点:microformats.org/wiki/rest/urls

@Beez mircoformats 链接为 servers 提供了一种构建其 URL 的好方法,但 server 决定了 URL。客户几乎永远不会这样做。如果您不理解这一点,请参阅 my answer 或相关的 article

P
Peter Mortensen

简短的回答:

简单的经验法则:使用 POST 创建,使用 PUT 更新。

长答案:

邮政:

POST 用于向服务器发送数据。

当资源的 URL 未知时很有用

放:

PUT 用于将状态传输到服务器

当资源的 URL 已知时很有用

更长的答案:

要理解它,我们需要质疑为什么需要 PUT,PUT 试图解决 POST 无法解决的问题是什么。

从 REST 架构的角度来看,这并不重要。我们也可以没有 PUT。但从客户开发人员的角度来看,这让他/她的生活变得简单多了。

在 PUT 之前,客户端无法直接知道服务器生成的 URL,或者它是否已经生成了任何 URL,或者要发送到服务器的数据是否已经更新。 PUT 让开发人员摆脱了所有这些头疼的问题。 PUT 是幂等的,PUT 处理竞争条件,PUT 让客户端选择 URL。

您的简短回答可能非常错误。 HTTP PUT 可以由 HTTP 代理免费重复。因此,如果 PUT 实际上是在执行 SQL INSERT,它可能会第二次失败,这意味着它将返回不同的结果,因此它不会是 IDEMPOTENT(这是 PUT 和 POST 之间的区别)

C
Community

新答案(现在我更了解 REST):

PUT 只是对从现在开始服务应该使用什么内容来呈现客户端标识的资源的表示的声明; POST 是关于从现在开始服务应该包含哪些内容(可能是重复的)的声明,但如何识别该内容取决于服务器。

PUT x(如果 x 标识了 resource):“将 x 标识的资源内容替换为我的内容。”

PUT x(如果 x 没有识别资源):“创建一个包含我的内容的新资源并使用 x 来识别它。”

POST x:“存储我的内容并给我一个标识符,我可以使用该标识符来识别包含所述内容(可能与其他内容混合)的资源(旧的或新的)。所述资源应与 x识别。” “y 的资源从属于 x 的资源”通常但不一定通过使 y 成为 x 的子路径来实现(例如 x = /fooy = /foo/bar)并修改 x 的表示资源以反映新资源的存在,例如带有到 y 的资源和一些元数据的超链接。只有后者对于良好的设计非常重要,因为 URL 在 REST 中是不透明的——您应该使用 use hypermedia 而不是客户端 URL 构造来遍历服务。

在 REST 中,不存在包含“内容”的资源。我将服务用于一致呈现表示的数据称为“内容”。它通常由数据库或文件(例如图像文件)中的一些相关行组成。由服务将用户的内容转换为服务可以使用的内容,例如将 JSON 有效负载转换为 SQL 语句。

原始答案(可能更容易阅读):

PUT /something(如果 /something 已经存在):“拿走你在 /something 的任何东西,然后用我给你的东西替换它。”

PUT /something(如果 /something 尚不存在):“把我给你的东西放在 /something 上。”

POST /something:“把我给你的东西拿去,把它放在 /something 下你想放的任何地方,只要你完成后给我它的 URL。”

但是,如果您的 ID 生成方法在 Auto Increment 上不存在,您如何使用 PUT 创建新资源?通常 ORM 会自动为您生成 ID,例如您希望它在 POST 中的方式。这是否意味着如果您想以正确的方式实现 PUT,您必须更改您的 id 自动生成?如果答案是肯定的,这很尴尬。

@RoniAxelrad:PUT 就像一个数据库“插入或更新”语句,您在语句中包含键,因此仅适用于您可以保证没有冲突的情况。例如。您的域有一个“自然键”,或者您使用的是 guid。 POST 就像使用自动递增键插入表中一样。数据库必须告诉您它在插入后得到的 ID。请注意,您的“插入或更新”将替换任何以前的数据(如果存在)。

@NigelThorne 感谢您的回答。因此,例如,如果我尝试使用 URI 放置一本书 id 10:PUT books/10。如果书 id 10 不存在,我应该创建一本 id 为 10 的书,对吗?但我无法控制创建 ID 分子,因为它是自动递增的。在那种情况下我该怎么办?

@RoniAxelrad REST PUT 到不存在的 ID 是对服务器创建资源的请求。仍然由服务器决定是否允许这样做。服务器负责。它可以回应“不。我不会那样做”。如果用户没有足够的权限...等,您已经这样做了。服务器说“不”是可以的。 REST 是一种约定,可以让我们定义各种类型请求的含义......您的服务器根据您的业务逻辑决定如何处理这些请求:) 即使它说“不”,它仍然遵循 REST :)

P
Peter Mortensen

Ruby on Rails 4.0 将使用“PATCH”方法而不是 PUT 来进行部分更新。

RFC 5789 提到了 PATCH(自 1995 年以来):

需要一种新方法来提高互操作性并防止错误。 PUT 方法已经定义为用一个完整的新主体覆盖资源,并且不能重用以进行部分更改。否则,代理和缓存,甚至客户端和服务器,可能会对操作的结果感到困惑。 POST 已经被使用,但没有广泛的互操作性(例如,没有标准的方法来发现补丁格式支持)。 PATCH 在早期的 HTTP 规范中提到过,但没有完全定义。

Edge Rails: PATCH is the new primary HTTP method for updates”解释它。

b
beginer

冒着重申已经说过的话的风险,记住 PUT 意味着客户端在创建资源时控制 URL 最终将成为什么似乎很重要。因此,在 PUT 和 POST 之间进行选择的一部分将是关于您可以信任客户端提供与您的 URL 方案是什么一致的正确、规范化的 URL 的程度。

当您不能完全信任客户端做正确的事情时,使用 POST 创建一个新项目,然后在响应中将 URL 发送回客户端会更合适。

我有点晚了 - 但是有人在另一个网站上说类似的话让它点击我。如果您正在创建资源并使用自动递增的 ID 作为“标识符”而不是用户分配的名称,则它应该是 POST。

这不太正确 - PUT 仍然可以通过使用非规范名称引用资源来创建资源,只要在响应中,服务器返回 确实 包含规范的资源名称。

@Joshcodes 不要忘记您可以有许多 URI 引用相同的底层资源。因此,Ether 所说的是合理的建议,客户端可以 PUT 到一个 URL(可能更语义化,如 PUT /X-files/series/4/episodes/max),服务器用一个 URI 响应,该 URI 提供到该新资源的简短规范唯一链接(即 /X-Ffiles/episodes/91 )

@thecoshman 问题是对 URL 结构的关注不属于客户端。阅读有关自我发现(也是 REST 的一部分)的内容可能有助于明确这一点。

@Joshcodes 然后按照这种逻辑,客户端永远不应该使用 PUT 来创建,因为他们不应该关心提供 URL。好吧......除非服务器提供了一个 URL 来 PUT 到如果客户端想要放入它......类似“PUT /comments/new”的东西,服务器可能会响应“204 /comments/234532”,但这似乎有点RPC 对我来说,客户端应该只是 POST 到 /comments...

R
Rohit Dhiman

除了其他人提出的差异之外,我想再添加一个。

POST 方法中,您可以在 form-data 中发送正文参数

PUT 方法中,您必须在 x-www-form-urlencoded 中发送正文参数

标题 Content-Type:application/x-www-form-urlencoded

据此,您不能在 PUT 方法中发送文件或多部分数据

编辑

内容类型“application/x-www-form-urlencoded”对于发送大量二进制数据或包含非 ASCII 字符的文本效率低下。内容类型“multipart/form-data”应用于提交包含文件、非 ASCII 数据和二进制数据的表单。

这意味着如果您必须提交

文件、非 ASCII 数据和二进制数据

你应该使用 POST 方法

为什么这没有被赞成?如果属实,这是一个关键的区别,不是吗?

我在为配置文件更新实现 API 时遇到了它,其中包括用户配置文件图片上传。然后我用 postman、Ajax、PHP curl 和 laravel 5.6 作为后端对其进行了测试。

P
Peter Mortensen

以一种非常简单的方式,我以 Facebook 时间线为例。

案例 1:当您在时间轴上发布某些内容时,这是一个全新的条目。所以在这种情况下,他们使用 POST 方法,因为 POST 方法是非幂等的。

案例 2:如果您的朋友第一次对您的帖子发表评论,那也会在数据库中创建一个新条目,因此使用 POST 方法。

案例 3:如果您的朋友编辑了他的评论,在这种情况下,他们有一个评论 ID,因此他们将更新现有评论,而不是在数据库中创建新条目。因此,对于这种类型的操作,请使用 PUT 方法,因为它是幂等的。*

在一行中,使用 POST 在数据库中添加新条目,并使用 PUT 更新数据库中的某些内容。

如果评论是一个具有用户 ID、创建日期、评论消息等属性的对象,并且在编辑时只有评论消息正在更新,那么 PATCH 应该在这里完成吗?

FB 使用 PUT 更新评论,因为正在更新现有资源,这就是 PUT 所做的(更新资源)。与 POST 相比,PUT 恰好是幂等的。幂等的 HTTP 动词会影响错误处理,但不指示用法。请参阅我的答案以获得更详细的说明:stackoverflow.com/questions/630453/put-vs-post-in-rest/…

H
Hans Malherbe

最重要的考虑是可靠性。如果 POST 消息丢失,则系统状态未定义。自动恢复是不可能的。对于 PUT 消息,仅在第一次成功重试之前状态未定义。

例如,使用 POST 创建信用卡交易可能不是一个好主意。

如果您的资源碰巧有自动生成的 URI,您仍然可以通过将生成的 URI(指向空资源)传递给客户端来使用 PUT。

其他一些考虑:

POST 使整个包含资源的缓存副本无效(更好的一致性)

PUT 响应不可缓存,而 POST 响应可缓存(需要 Content-Location 和过期)

PUT 较少受到 Java ME、旧浏览器、防火墙等的支持

这是不正确的。对于 POST,状态也是未定义的,直到第一次成功重试。然后,服务器要么接受 POST(消息从未到达),要么为重复的 ID 引发 409 冲突(消息到达,响应丢失),或者任何其他有效响应。

一般来说,用户代理不能安全地重试 POST 操作,因为 POST 操作不能保证两个操作与一个操作具有相同的效果。术语“ID”与 HTTP 无关。 URI 标识资源。

用户代理可以“安全地”重试 POST 操作,次数不限。它只会收到重复的 ID 错误(假设资源有 ID)或重复的数据错误(假设这是一个问题并且资源没有 ID)。

刘海头撞墙。 HTTP 没有解决可靠性问题的方法,而且这个问题没有得到很好的理解,也没有太多的讨论,而且在绝大多数 Web 应用程序中根本不适合。 @Joshcodes 我有这个问题的答案。我基本上同意汉斯的观点。有问题。

@bbsimonbb,HTTP 有一组健壮且有据可查的错误响应。我对这个问题 (stackoverflow.com/questions/630453/put-vs-post-in-rest/…) 的回答包括如何根据规范使用 http 来实现一致性。

b
bbsimonbb

刚接触这个主题的读者会被关于你应该做什么的无休止的讨论以及相对缺乏经验教训所震惊。我认为 REST 优于 SOAP 的事实是从经验中学习的高级别的,但是我们一定是从那里取得进步的吧?现在是 2016 年。Roy 的论文是在 2000 年。我们开发了什么?它有趣吗?容易集成吗?支持?它会处理智能手机的兴起和不稳定的移动连接吗?

根据 ME 的说法,现实生活中的网络是不可靠的。请求超时。连接被重置。网络一次中断数小时或数天。火车与移动用户一起进入隧道。对于任何给定的请求(正如在所有讨论中偶尔承认的那样),请求可能在途中落入水中,或者响应可能在返回途中落入水中。在这种情况下,直接针对实质性资源发出 PUT、POST 和 DELETE 请求总是让我觉得有点野蛮和幼稚。

HTTP 没有做任何事情来确保请求-响应的可靠完成,这很好,因为这正是网络感知应用程序的工作。开发这样的应用程序,您可以跳过箍以使用 PUT 而不是 POST,然后如果检测到重复请求,则更多箍在服务器上给出某种错误。回到客户端,您必须跳过障碍来解释这些错误、重新获取、重新验证和重新发布。

或者您可以这样做:将您的不安全请求视为短暂的单用户资源(我们称它们为操作)。客户端通过对资源的空 POST 请求对实质性资源执行新的“操作”。 POST 将仅用于此目的。一旦安全地拥有新生成的操作的 URI,客户端会将不安全的请求放入操作 URI,而不是目标资源。解决操作和更新“真实”资源是您的 API 的正确工作,并且在这里与不可靠的网络分离。

服务器执行业务,返回响应并将其存储在约定的操作 URI 上。如果出现任何问题,客户端会重复请求(自然行为!),如果服务器已经看到它,它会重复存储的响应并且不执行任何其他操作。

您将很快发现与 promise 的相似之处:我们在执行任何操作之前创建并返回结果的占位符。也像一个承诺,一个动作可以成功或失败一次,但它的结果可以重复获取。

最重要的是,我们为发送和接收应用程序提供了将唯一标识的操作与各自环境中的唯一性相关联的机会。我们可以开始要求并强制执行!客户负责任的行为:尽可能多地重复您的请求,但在您拥有现有请求的明确结果之前不要生成新的操作。

这样,许多棘手的问题就消失了。重复的插入请求不会创建重复,并且在我们拥有数据之前我们不会创建真正的资源。 (数据库列可以保持不可为空)。重复的更新请求不会达到不兼容的状态,也不会覆盖后续的更改。无论出于何种原因(客户端崩溃、响应丢失等),客户端都可以(重新)获取并无缝处理原始确认。

连续的删除请求可以查看和处理原始确认,而不会遇到 404 错误。如果事情花费的时间比预期的要长,我们可以临时做出回应,并且我们有一个地方可以让客户检查最终结果。这种模式最好的部分是它的功夫(熊猫)属性。我们采取弱点,即客户在不理解响应时重复请求的倾向,并将其转化为优势:-)

在告诉我这不是 RESTful 之前,请考虑尊重 REST 原则的多种方式。客户端不构建 URL。 API 保持可发现性,尽管在语义上有一些变化。 HTTP 动词使用得当。如果您认为这是一个巨大的实施变化,我可以根据经验告诉您事实并非如此。

如果您认为要存储大量数据,让我们来谈谈容量:典型的更新确认只有千字节的一小部分。 HTTP 当前给您一两分钟的时间来明确响应。即使您只存储一周的操作,客户也有足够的机会赶上。如果您的容量非常大,您可能需要一个专用的符合 acid 的键值存储或内存解决方案。

存储响应不会像维护会话一样吗?这会导致(水平)缩放问题。

B
Burhan

对于 REST 服务何时使用 HTTP POST 与 HTTP PUT 方法,似乎总是有些混淆。大多数开发人员会尝试将 CRUD 操作直接关联到 HTTP 方法。我认为这是不正确的,不能简单地将 CRUD 概念与 HTTP 方法相关联。那是:

Create => HTTP PUT
Retrieve => HTTP GET
Update => HTTP POST
Delete => HTTP DELETE

确实,CRUD 操作的 R(etrieve) 和 D(elete) 可以分别直接映射到 HTTP 方法 GET 和 DELETE。然而,混淆在于 C(reate) 和 U(update) 操作。在某些情况下,可以使用 PUT 进行创建,而在其他情况下则需要 POST。歧义在于 HTTP PUT 方法与 HTTP POST 方法的定义。

根据 HTTP 1.1 规范,GET、HEAD、DELETE 和 PUT 方法必须是幂等的,而 POST 方法不是幂等的。也就是说,一个操作是幂等的,如果它可以在一个资源上执行一次或多次并且总是返回该资源的相同状态。而非幂等操作可以将资源的修改状态从一个请求返回到另一个请求。因此,在非幂等操作中,不能保证一定会收到相同的资源状态。

基于上述幂等定义,我对 REST 服务使用 HTTP PUT 方法与使用 HTTP POST 方法的看法是: 在以下情况下使用 HTTP PUT 方法:

The client includes all aspect of the resource including the unique identifier to uniquely identify the resource. Example: creating a new employee.
The client provides all the information for a resource to be able to modify that resource.This implies that the server side does not update any aspect of the resource (such as an update date).

在这两种情况下,可以多次执行这些操作并获得相同的结果。也就是说,资源不会因多次请求操作而改变。因此,一个真正的幂等操作。在以下情况下使用 HTTP POST 方法:

The server will provide some information concerning the newly created resource. For example, take a logging system. A new entry in the log will most likely have a numbering scheme which is determined on the server side. Upon creating a new log entry, the new sequence number will be determined by the server and not by the client.
On a modification of a resource, the server will provide such information as a resource state or an update date. Again in this case not all information was provided by the client and the resource will be changing from one modification request to the next. Hence a non idempotent operation.

结论

不要直接将 CRUD 操作关联和映射到 REST 服务的 HTTP 方法。 HTTP PUT 方法与 HTTP POST 方法的使用应基于该操作的幂等性方面。也就是说,如果操作是幂等的,则使用 HTTP PUT 方法。如果操作是非幂等的,则使用 HTTP POST 方法。

更新 => HTTP POST : POST 不用于更新

@premraj您假设Burhan告诉您不要这样做;即,您将 CRUD、REST 和 HTTP 混为一谈。如果您阅读 RFC 7231,其中定义了这些内容,您会发现在 HTTP 协议中,POST 的定义当然允许更新。只有 REST 的约束另有说明。

P
Peter Mortensen

源服务器可以使用该 URI 创建资源

因此,您使用 POST 和可能但不是必需的 PUT 来创建资源。您不必同时支持两者。对我来说,POST 就足够了。所以这是一个设计决定。

正如您的报价所提到的,您使用 PUT 来创建没有分配给 IRI 的资源,并且无论如何您都想创建一个资源。例如,PUT /users/123/password 通常用新密码替换旧密码,但如果密码不存在(例如,新注册的用户或恢复被禁止的用户),您可以使用它来创建密码。

我认为您已经设法提供了少数几个如何使用 PUT 的好例子之一,做得好。

P
Peter Mortensen

我将登陆以下内容:

PUT 指的是由 URI 标识的资源。在这种情况下,您正在更新它。它是指资源的三个动词的一部分——删除和获取是另外两个。

POST 基本上是一个自由格式的消息,其含义被定义为“带外”。如果消息可以被解释为向目录添加资源,那没关系,但基本上您需要了解您正在发送(发布)的消息以了解资源会发生什么。

因为 PUT 和 GET 和 DELETE 指的是一个资源,所以它们在定义上也是幂等的。

POST 可以执行其他三个功能,但是请求的语义将在缓存和代理等中介上丢失。这也适用于为资源提供安全性,因为帖子的 URI 不一定指示它正在应用的资源(尽管它可以)。

一个 PUT 不需要是一个创建;如果资源尚未创建,服务可能会出错,否则更新它。反之亦然——它可以创建资源,但不允许更新。 PUT 唯一需要的是它指向一个特定的资源,它的有效负载是该资源的表示。成功的 PUT 意味着(排除干扰)GET 将检索相同的资源。

编辑:还有一件事——一个 PUT 可以创建,但如果它创建了,那么 ID 必须是一个自然 ID——也就是一个电子邮件地址。这样,当您 PUT 两次时,第二次 put 是第一次的更新。这使它具有幂等性。

如果生成了 ID(例如新的员工 ID),则具有相同 URL 的第二个 PUT 将创建一个新记录,这违反了幂等规则。在这种情况下,动词是 POST,消息(不是资源)是使用该消息中定义的值创建资源。

P
Peter Mortensen

这是一个简单的规则:

应使用 PUT 到 URL 来更新或创建可位于该 URL 的资源。

POST 到 URL 应该用于更新或创建位于某个其他(“从属”)URL 或无法通过 HTTP 定位的资源。

PUT 不是用于更新,而是用于替换,请注意,创建您不会用某些东西替换任何内容。 POST 绝对不用于任何形式的更新。

http规范有这样说吗?还是您的评论基于其他内容?

thecoshman - 你在这里滥用语义 - 如果替换是相同的资源但有一些差异,则替换可以是更新。仅当使用 replace 更改同一资源时,replace 才对 put 有效。用新的和不同的资源替换是无效的(删除旧的并添加新的?),特别是如果“新”资源没有自然 ID。 POST,OTOH,是可以创建、更新、替换和删除的东西——使用 post 取决于是否有要解释的消息,例如“应用折扣”,这可能会或可能不会更改资源,具体取决于逻辑。

至于您的第二条评论 - 您如何“获取”资源,修改您需要的字段,然后将其放回原处?或者如果资源来自不同的来源但使用自然 ID(外部 ID)——当原始数据更改时,put 会自然地更新 URL 处的资源。

G
Gregory Magarshak

语义应该是不同的,因为“PUT”和“GET”一样应该是幂等的——这意味着,您可以多次执行相同的 PUT 请求,结果就像您只执行了一次一样。

我将描述我认为最广泛使用和最有用的约定:

当您将资源放在特定的 URL 时,会发生这种情况,它应该保存在该 URL 或类似的东西上。

当您发布到特定 URL 的资源时,您通常会将相关信息发布到该 URL。这意味着 URL 处的资源已经存在。

例如,当你想创建一个新流时,你可以把它放到某个 URL 上。但是,当您想将消息发布到现有流时,您需要发布到其 URL。

至于修改流的属性,您可以使用 PUT 或 POST 来完成。基本上,只有在操作是幂等的时候才使用“PUT”——否则使用 POST。

但是请注意,并非所有现代浏览器都支持 GET 或 POST 以外的 HTTP 动词。

您将 POST 描述为实际上 PATCH 应该如何表现。 POST 应该意味着更类似于“附加”,如“发布到邮件列表”。

t
tothemario

大多数时候,你会像这样使用它们:

将资源发布到集合中

PUT 由 collection/:id 标识的资源

例如:

发布/项目

放置 /items/1234

在这两种情况下,请求正文都包含要创建或更新的资源的数据。从路由名称中应该可以明显看出 POST 不是幂等的(如果调用 3 次将创建 3 个对象),但 PUT 是幂等的(如果调用 3 次结果相同)。 PUT 通常用于“upsert”操作(创建或更新),但如果您只想使用它进行修改,则始终可以返回 404 错误。

请注意,POST 在集合中“创建”一个新元素,而 PUT 在给定 URL 处“替换”一个元素,但使用 PUT 进行部分修改是一种非常常见的做法,即仅使用它来更新现有资源和仅修改正文中包含的字段(忽略其他字段)。这在技术上是不正确的,如果你想成为 REST 纯粹主义者,PUT 应该替换整个资源,你应该使用 PATCH 进行部分更新。只要行为在所有 API 端点中清晰且一致,我个人并不在意。

请记住,REST 是一组让您的 API 保持简单的约定和指南。如果您最终只是为了检查“RESTfull”框而进行了复杂的解决方法,那么您就没有达到目的;)

M
Moritz

对我来说,理解差异的关键是了解谁定义了资源的 ID:

示例(带有一些地址服务)

POST (sever creates new resource)

client               server/addresses      // NOTE: no ID in the request
  |                                 |
  | --{POST address data}-->        |
  |                                 |
  | <--{201, created addresses/321} |      // NOTE: resource ID in the reply
  |                                 |
PUT (sever sets data of resource, creating it if necessary)

client               server/addresses/321      // NOTE: *you* put the ID here!
  |                                 |
  | --{PUT address data (to 321)}-->|
  |                                 |
  | <--{201, created }              |          // NOTE: resource ID not required here
  |                                 |

下面有很多很棒的答案,里面有很多细节,但这帮助我明白了这一点。

R
Rajan

如果你熟悉数据库操作,有

选择插入更新删除合并(如果已经存在则更新,否则插入)

我将 PUT 用于合并和更新类似操作,并将 POST 用于插入。

P
Peter Mortensen

虽然可能有一种不可知论的方式来描述这些,但它似乎与来自网站答案的各种陈述相冲突。

让我们在这里非常清楚和直接。如果您是使用 Web API 的 .NET 开发人员,事实是(来自 Microsoft API 文档),http://www.asp.net/web-api/overview/creating-web-apis/creating-a-web-api-that-supports-crud-operations

1. PUT = UPDATE (/api/products/id)
2. MCSD Exams 2014 -  UPDATE = PUT, there are **NO** multiple answers for that question period.

当然,您“可以”使用“POST”进行更新,但只需按照给定框架为您制定的约定即可。就我而言,它是 .NET / Web API,所以 PUT 用于 UPDATE 没有争议。

我希望这可以帮助任何使用 Amazon 和 Sun/Java 网站链接阅读所有评论的 Microsoft 开发人员。