根据 RFC 2616, § 9.5,POST
用于创建一个资源:
POST 方法用于请求源服务器接受请求中包含的实体,作为 Request-Line 中 Request-URI 标识的资源的新下级。
根据 RFC 2616, § 9.6,PUT
用于创建或替换资源:
PUT 方法请求将封闭的实体存储在提供的 Request-URI 下。如果 Request-URI 引用了一个已经存在的资源,封闭的实体应该被认为是在源服务器上的一个修改版本。如果 Request-URI 不指向现有资源,并且该 URI 能够被请求用户代理定义为新资源,则源服务器可以使用该 URI 创建资源。
那么应该使用哪种 HTTP 方法来创建资源呢?还是应该同时支持?
全面的:
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/
此外,更简洁一点的是,RFC 7231 Section 4.3.4 PUT 状态(添加了重点),
4.3.4. PUT PUT 方法请求创建目标资源的状态或将其替换为请求消息有效负载中包含的表示定义的状态。
你可以在网上找到断言说
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
name
和 date
。如果我们有一个具有现有 name
和 date
的实体,但随后仅指定 name
向它发出请求,则 PUT 的正确行为将是删除 date
实体,而 POST 可能只更新指定的属性,将未指定的属性保留为发出请求之前的状态。这听起来正确/合理,还是对 PUT 的不当使用(我看到了对 PATCH 的引用,这似乎更合适,但还不存在)?
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 更好地匹配。当工作异步完成时,发布转换并等待它被应用似乎是合适的。
POST
表示“创建新”,如“这是创建用户的输入,为我创建”。
PUT
表示“插入,如果已存在则替换”,如“这是用户 5 的数据”。
您 POST
到 example.com/users,因为您还不知道用户的 URL
,您希望服务器创建它。
您PUT
访问 example.com/users/id,因为您想替换/创建一个特定用户。
使用相同的数据发布两次意味着创建两个具有不同 ID 的相同用户。使用相同的数据进行两次 PUT 会创建第一个用户,第二次将他更新到相同的状态(无更改)。由于无论执行多少次 PUT
之后都会以相同的状态结束,因此它被称为每次都“同等有效” - 幂等。这对于自动重试请求很有用。当您按下浏览器上的后退按钮时,不再有“您确定要重新发送”。
当您需要服务器控制资源的 URL
生成时,一般建议是使用 POST
。否则使用 PUT
。首选 PUT
而不是 POST
。
user 5
中?你不是说update, replace if already exists
吗?或者其他的东西
概括:
创造:
可以通过以下方式使用 PUT 或 POST 执行:
PUT 在 /resources URI 或集合下以 newResourceId 作为标识符创建新资源。 PUT /resources/
更新:
只能通过以下方式使用 PUT 执行:
PUT 在 /resources URI 或集合下以 existingResourceId 作为标识符更新资源。 PUT /resources/
解释:
当一般处理 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):通用语法和语义
我想补充一下我的“务实”建议。当您知道可以检索所保存对象的“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
集合的子资源的效果。
GET /users
是有道理的,它可以按照您的意愿读取,但我可以使用 GET /user/<id>
或 POST /user
(带有所述新用户的有效负载),因为它正确读取 'get me users 5' 很奇怪,但是 'get me 用户 5' 更自然。不过,我可能仍然会支持多元化:)
GET /users/5/documents/4/title
就像'获取用户,从那里获取用户 5,从那里获取文档,从那里获取文档 4,从那里给我标题'
两者都用于客户端到服务器之间的数据传输,但它们之间存在细微差别,分别是:
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
社交媒体/网络类比:
在社交媒体上发布:当我们发布消息时,它会创建新帖子。
为我们已经发布的消息放置(即编辑)。
使用 POST 创建,使用 PUT 更新。无论如何,Ruby on Rails 就是这样做的。
PUT /items/1 #=> update
POST /items #=> create
POST /items
将新项目添加到已定义的资源(“项目”)。正如答案所说,它不会“创建一个组”。我不明白为什么这有 12 票。
PUT /items/42
对于创建资源也是有效的,但前提是客户端有权命名资源。 (Rails 是否允许客户端这种命名特权?)
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 还是 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。
我喜欢来自 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 结构做出假设,因此这不符合预期的精神。
简而言之:
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 执行更新。没有硬性规定哪一个用于更新或哪一个用于创建。这些都是惯例,直觉上我倾向于上述推理并遵循它。
POST 就像将一封信发送到邮箱或将电子邮件发送到电子邮件队列。 PUT 就像你把一个物体放在一个小房间或架子上的一个地方(它有一个已知的地址)。
使用 POST,您将发布到 QUEUE 或 COLLECTION 的地址。使用 PUT,您将放入 ITEM 的地址。
PUT 是幂等的。您可以发送请求 100 次,这无关紧要。 POST 不是幂等的。如果您发送请求 100 次,您将在邮箱中收到 100 封电子邮件或 100 封信件。
一般规则:如果您知道项目的 id 或名称,请使用 PUT。如果您希望接收方分配项目的 id 或名称,请使用 POST。
https://i.stack.imgur.com/ugbjx.png
简短的回答:
简单的经验法则:使用 POST 创建,使用 PUT 更新。
长答案:
邮政:
POST 用于向服务器发送数据。
当资源的 URL 未知时很有用
放:
PUT 用于将状态传输到服务器
当资源的 URL 已知时很有用
更长的答案:
要理解它,我们需要质疑为什么需要 PUT,PUT 试图解决 POST 无法解决的问题是什么。
从 REST 架构的角度来看,这并不重要。我们也可以没有 PUT。但从客户开发人员的角度来看,这让他/她的生活变得简单多了。
在 PUT 之前,客户端无法直接知道服务器生成的 URL,或者它是否已经生成了任何 URL,或者要发送到服务器的数据是否已经更新。 PUT 让开发人员摆脱了所有这些头疼的问题。 PUT 是幂等的,PUT 处理竞争条件,PUT 让客户端选择 URL。
新答案(现在我更了解 REST):
PUT 只是对从现在开始服务应该使用什么内容来呈现客户端标识的资源的表示的声明; POST 是关于从现在开始服务应该包含哪些内容(可能是重复的)的声明,但如何识别该内容取决于服务器。
PUT x
(如果 x
标识了 resource):“将 x
标识的资源内容替换为我的内容。”
PUT x
(如果 x
没有识别资源):“创建一个包含我的内容的新资源并使用 x
来识别它。”
POST x
:“存储我的内容并给我一个标识符,我可以使用该标识符来识别包含所述内容(可能与其他内容混合)的资源(旧的或新的)。所述资源应与 x
识别。” “y 的资源从属于 x 的资源”通常但不一定通过使 y 成为 x 的子路径来实现(例如 x = /foo
和 y = /foo/bar
)并修改 x 的表示资源以反映新资源的存在,例如带有到 y 的资源和一些元数据的超链接。只有后者对于良好的设计非常重要,因为 URL 在 REST 中是不透明的——您应该使用 use hypermedia 而不是客户端 URL 构造来遍历服务。
在 REST 中,不存在包含“内容”的资源。我将服务用于一致呈现表示的数据称为“内容”。它通常由数据库或文件(例如图像文件)中的一些相关行组成。由服务将用户的内容转换为服务可以使用的内容,例如将 JSON 有效负载转换为 SQL 语句。
原始答案(可能更容易阅读):
PUT /something
(如果 /something
已经存在):“拿走你在 /something
的任何东西,然后用我给你的东西替换它。”
PUT /something
(如果 /something
尚不存在):“把我给你的东西放在 /something
上。”
POST /something
:“把我给你的东西拿去,把它放在 /something
下你想放的任何地方,只要你完成后给我它的 URL。”
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”解释它。
冒着重申已经说过的话的风险,记住 PUT 意味着客户端在创建资源时控制 URL 最终将成为什么似乎很重要。因此,在 PUT 和 POST 之间进行选择的一部分将是关于您可以信任客户端提供与您的 URL 方案是什么一致的正确、规范化的 URL 的程度。
当您不能完全信任客户端做正确的事情时,使用 POST 创建一个新项目,然后在响应中将 URL 发送回客户端会更合适。
PUT /X-files/series/4/episodes/max
),服务器用一个 URI 响应,该 URI 提供到该新资源的简短规范唯一链接(即 /X-Ffiles/episodes/91
)
除了其他人提出的差异之外,我想再添加一个。
在 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 方法
以一种非常简单的方式,我以 Facebook 时间线为例。
案例 1:当您在时间轴上发布某些内容时,这是一个全新的条目。所以在这种情况下,他们使用 POST 方法,因为 POST 方法是非幂等的。
案例 2:如果您的朋友第一次对您的帖子发表评论,那也会在数据库中创建一个新条目,因此使用 POST 方法。
案例 3:如果您的朋友编辑了他的评论,在这种情况下,他们有一个评论 ID,因此他们将更新现有评论,而不是在数据库中创建新条目。因此,对于这种类型的操作,请使用 PUT 方法,因为它是幂等的。*
在一行中,使用 POST 在数据库中添加新条目,并使用 PUT 更新数据库中的某些内容。
最重要的考虑是可靠性。如果 POST 消息丢失,则系统状态未定义。自动恢复是不可能的。对于 PUT 消息,仅在第一次成功重试之前状态未定义。
例如,使用 POST 创建信用卡交易可能不是一个好主意。
如果您的资源碰巧有自动生成的 URI,您仍然可以通过将生成的 URI(指向空资源)传递给客户端来使用 PUT。
其他一些考虑:
POST 使整个包含资源的缓存副本无效(更好的一致性)
PUT 响应不可缓存,而 POST 响应可缓存(需要 Content-Location 和过期)
PUT 较少受到 Java ME、旧浏览器、防火墙等的支持
刚接触这个主题的读者会被关于你应该做什么的无休止的讨论以及相对缺乏经验教训所震惊。我认为 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 的键值存储或内存解决方案。
对于 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 方法。
源服务器可以使用该 URI 创建资源
因此,您使用 POST 和可能但不是必需的 PUT 来创建资源。您不必同时支持两者。对我来说,POST 就足够了。所以这是一个设计决定。
正如您的报价所提到的,您使用 PUT 来创建没有分配给 IRI 的资源,并且无论如何您都想创建一个资源。例如,PUT /users/123/password
通常用新密码替换旧密码,但如果密码不存在(例如,新注册的用户或恢复被禁止的用户),您可以使用它来创建密码。
我将登陆以下内容:
PUT 指的是由 URI 标识的资源。在这种情况下,您正在更新它。它是指资源的三个动词的一部分——删除和获取是另外两个。
POST 基本上是一个自由格式的消息,其含义被定义为“带外”。如果消息可以被解释为向目录添加资源,那没关系,但基本上您需要了解您正在发送(发布)的消息以了解资源会发生什么。
因为 PUT 和 GET 和 DELETE 指的是一个资源,所以它们在定义上也是幂等的。
POST 可以执行其他三个功能,但是请求的语义将在缓存和代理等中介上丢失。这也适用于为资源提供安全性,因为帖子的 URI 不一定指示它正在应用的资源(尽管它可以)。
一个 PUT 不需要是一个创建;如果资源尚未创建,服务可能会出错,否则更新它。反之亦然——它可以创建资源,但不允许更新。 PUT 唯一需要的是它指向一个特定的资源,它的有效负载是该资源的表示。成功的 PUT 意味着(排除干扰)GET 将检索相同的资源。
编辑:还有一件事——一个 PUT 可以创建,但如果它创建了,那么 ID 必须是一个自然 ID——也就是一个电子邮件地址。这样,当您 PUT 两次时,第二次 put 是第一次的更新。这使它具有幂等性。
如果生成了 ID(例如新的员工 ID),则具有相同 URL 的第二个 PUT 将创建一个新记录,这违反了幂等规则。在这种情况下,动词是 POST,消息(不是资源)是使用该消息中定义的值创建资源。
这是一个简单的规则:
应使用 PUT 到 URL 来更新或创建可位于该 URL 的资源。
POST 到 URL 应该用于更新或创建位于某个其他(“从属”)URL 或无法通过 HTTP 定位的资源。
语义应该是不同的,因为“PUT”和“GET”一样应该是幂等的——这意味着,您可以多次执行相同的 PUT 请求,结果就像您只执行了一次一样。
我将描述我认为最广泛使用和最有用的约定:
当您将资源放在特定的 URL 时,会发生这种情况,它应该保存在该 URL 或类似的东西上。
当您发布到特定 URL 的资源时,您通常会将相关信息发布到该 URL。这意味着 URL 处的资源已经存在。
例如,当你想创建一个新流时,你可以把它放到某个 URL 上。但是,当您想将消息发布到现有流时,您需要发布到其 URL。
至于修改流的属性,您可以使用 PUT 或 POST 来完成。基本上,只有在操作是幂等的时候才使用“PUT”——否则使用 POST。
但是请注意,并非所有现代浏览器都支持 GET 或 POST 以外的 HTTP 动词。
大多数时候,你会像这样使用它们:
将资源发布到集合中
PUT 由 collection/:id 标识的资源
例如:
发布/项目
放置 /items/1234
在这两种情况下,请求正文都包含要创建或更新的资源的数据。从路由名称中应该可以明显看出 POST 不是幂等的(如果调用 3 次将创建 3 个对象),但 PUT 是幂等的(如果调用 3 次结果相同)。 PUT 通常用于“upsert”操作(创建或更新),但如果您只想使用它进行修改,则始终可以返回 404 错误。
请注意,POST 在集合中“创建”一个新元素,而 PUT 在给定 URL 处“替换”一个元素,但使用 PUT 进行部分修改是一种非常常见的做法,即仅使用它来更新现有资源和仅修改正文中包含的字段(忽略其他字段)。这在技术上是不正确的,如果你想成为 REST 纯粹主义者,PUT 应该替换整个资源,你应该使用 PATCH 进行部分更新。只要行为在所有 API 端点中清晰且一致,我个人并不在意。
请记住,REST 是一组让您的 API 保持简单的约定和指南。如果您最终只是为了检查“RESTfull”框而进行了复杂的解决方法,那么您就没有达到目的;)
对我来说,理解差异的关键是了解谁定义了资源的 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
| |
下面有很多很棒的答案,里面有很多细节,但这帮助我明白了这一点。
如果你熟悉数据库操作,有
选择插入更新删除合并(如果已经存在则更新,否则插入)
我将 PUT
用于合并和更新类似操作,并将 POST
用于插入。
虽然可能有一种不可知论的方式来描述这些,但它似乎与来自网站答案的各种陈述相冲突。
让我们在这里非常清楚和直接。如果您是使用 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 开发人员。