HTTP 中 POST 和 PUT 请求有什么区别?

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

POST方法用于请求源服务器接受请求中包含的实体作为请求行中请求URI标识的资源的新从属。

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

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

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

3094931 次浏览

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

PUT    /items/1      #=> updatePOST   /items        #=> create

总体而言:

PUT和POST都可以用于创建。

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

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

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

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

一些注意事项:

  • 您是显式命名您创建的URL对象,还是让服务器决定?如果您命名它们,那么使用PUT。如果您让服务器决定,那么使用POST。
  • PUT被定义为假设幂等性,所以如果你把一个对象放两次,它应该没有额外的效果。这是一个很好的属性,所以我会尽可能使用PUT。只要确保PUT-幂等性在服务器中得到正确实现。
  • 您可以使用具有相同对象URL的PUT更新或创建资源
  • 使用POST,您可以同时有两个请求对URL进行修改,它们可能会更新对象的不同部分。

举个例子:

我写了以下作为关于这个问题的回答的一部分:

帖子:

用于修改和更新资源

POST /questions/<existing_question> HTTP/1.1Host: www.example.com/

请注意,以下是一个错误:

POST /questions/<new_question> HTTP/1.1Host: www.example.com/

如果URL尚未创建,则不应该使用POST来创建它同时指定名称。这应该导致“未找到资源”错误因为<new_question>不存在还没有。你应该把<new_question>服务器上的资源。

你可以这样做使用POST创建资源:

POST /questions HTTP/1.1Host: www.example.com/

请注意,在这种情况下,资源名称未指定,新对象URL路径将返回给您。

放:

用于创建资源,或覆盖它。当您指定资源新URL。

对于新资源:

PUT /questions/<new_question> HTTP/1.1Host: www.example.com/

要覆盖现有资源:

PUT /questions/<existing_question> HTTP/1.1Host: www.example.com/

此外,更简洁地说,RFC 7231第4.3.4节PUT状态(强调添加),

4.3.4.把

PUT方法请求目标资源的状态为createdreplaced,状态由表示定义包含在请求消息有效负载中。

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

如果您对如何在HTTP中实现REST有任何疑问,您可以随时查看原子发布协议(Atom Pub)规范。原子发布是使用HTTP编写RESTful Web服务的标准,由许多HTTP和REST名人开发,并由REST的发明者和HTTP本人(共同)发明者Roy Fielding提供了一些输入。

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

这是关于资源创建的原子库(第9.2节):

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

  • POST服务器定义 URL的URL创建子资源
  • 客户端定义 URL处的整个URL创建/替换资源
  • 补丁到客户端定义的URL更新资源的部分

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

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

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

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

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

RFC是这样写的:

POST和PUT请求之间的根本区别反映在请求URI的不同含义上。POST请求中的URI标识了将处理封闭实体的资源。该资源可能是数据接受进程、其他协议的网关或接受注释的单独实体。相比之下,PUT请求中的URI标识了请求封闭的实体——用户代理知道预期的URI是什么,服务器不得尝试将请求应用于其他资源。如果服务器希望将请求应用于不同的URI,

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

更新2018:有一种情况可以避免PUT。参见"REST没有PUT"

使用“REST没有PUT”技术,这个想法是消费者被迫发布新的“命名”请求资源。如所讨论的前面,更改客户的邮寄地址是一个POST到一个新的“ChangeOfAddress”资源,而不是“客户”资源的PUT不同的邮件地址字段值。

取自REST API设计-Thoughtworks的Prakash Subramanam的资源建模

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

你可以在网上找到断言

两者都不完全正确。


最好是根据动作的幂等性在PUT和POST之间进行选择。

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

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


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

所以:

POST /expense-report

或:

PUT  /expense-report/10929

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

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

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

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

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

冒着重复已经说过的话的风险,似乎重要的是要记住意味着客户端在创建资源时控制url的最终结果。因此,在POST之间做出选择的一部分将是您可以在多大程度上信任客户端提供与您的URL方案一致的正确、标准化的url

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

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

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

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

PUTexample.com/users/id,因为您要替换/创建特定用户。

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

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

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

我将描述我认为最广泛使用和最有用的惯例:

当您将资源放在特定URL时,会发生的情况是它应该保存在该URL或类似的位置。

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

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

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

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

我喜欢这个建议,来自RFC 2616对PUT的定义

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

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

我解释这一点,以及PUT上的幂等性要求,意味着:

  • POST对于在集合下创建新对象很有用(创建不需要是幂等的)
  • PUT适用于更新现有对象(并且更新需要是幂等的)
  • POST也可以用于对现有对象的非幂等更新(特别是,在不指定整个对象的情况下更改对象的一部分-如果您考虑一下,从集合的角度来看,创建集合的新成员实际上是这种更新的特例)
  • 当且仅当您允许客户端命名资源时,PUT也可用于创建。但是由于REST客户端不应该对URL结构做出假设,因此这不太符合预期的精神。

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

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

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

其他一些考虑:

  • POST使整个包含资源的缓存副本无效(更好的一致性)
  • PUT响应不可缓存,而POST响应可缓存(需要内容位置和过期)
  • PUT不太受支持,例如JavaME,旧浏览器,防火墙

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

RFC 5789关于PATCH(自1995年以来):

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

Edge Rails:PATCH是更新的新主要HTTP方法”表示。

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

PUT仅仅是服务应该从现在开始使用什么内容来呈现客户端识别的资源的表示的语句;POST是服务应该从现在开始包含(可能是重复的)什么内容的语句,但由服务器如何识别该内容。

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

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

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

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

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

PUT /something(如果/something已经存在):“拿你在/something的任何东西,用我给你的东西代替。

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

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

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

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

PUT是幂等的。您可以发送请求100次,这并不重要。POST不是幂等的。如果您发送请求100次,您的邮政信箱中会收到100封电子邮件或100封信。

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

POST对PUT

总结:

创建:

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

创建新资源,以资源名称作为标识符,在 /resourcesURI或收藏下。

PUT /resources/<newResourceId> HTTP/1.1

POST

在 /resourcesURI或收藏下创建一个新资源。通常标识符由服务器返回。

POST /resources HTTP/1.1

更新时间:

可以通过以下方式使用PUT执行只有

是否存在资源类型作为标识符更新资源,在 /resourcesURI或收藏下。

PUT /resources/<existingResourceId> HTTP/1.1

说明:

一般处理REST和URI时,您在上有仿制药,在上有特定泛型通常称为收藏,更多的特定项可以称为资源。请注意,资源可以包含收藏

示例:

<--通用--特定-->

URI: website.example/users/johnwebsite.example  - whole siteusers        - collection of usersjohn         - item of the collection, or a resource
URI:website.example/users/john/posts/23website.example  - whole siteusers        - collection of usersjohn         - item of the collection, or a resourceposts        - collection of posts from john23           - post from john with identifier 23, also a resource

当你使用POST时,你是总是引用收藏,所以每当你说:

POST /users HTTP/1.1

您正在向用户收藏发布新用户。

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

POST /users/john HTTP/1.1

它会起作用,但从语义上讲,您是说要将资源添加到用户收藏下的约翰收藏

一旦你使用PUT,你就会引用资源或单个项目,可能在收藏中。所以当你说:

PUT /users/john HTTP/1.1

您正在告诉服务器更新,或者如果它不存在,则创建用户收藏下的约翰资源

规格:

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

POST

POST方法用于请求源服务器接受将请求中包含的实体作为请求行中请求URI标识的资源的new下属

因此,在收藏上创建一个新的资源

方法要求封闭实体在提供的请求URI下为存储。如果请求URI引用已经存在资源,则封闭实体应被视为驻留在源服务器上的实体中的修改版本。如果请求URI做了不指向现有资源,并且该URI是有能力,请求用户代理将其定义为new资源,则源服务器可以使用该URI创建资源。”

因此,基于资源的存在创建或更新。

参考:

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

Create => HTTP PUTRetrieve => HTTP GETUpdate => HTTP POSTDelete => HTTP DELETE

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

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

基于上述幂等定义,我对使用HTTP PUT方法与使用HTTP POST方法进行REST服务的看法是:在以下情况下使用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方法。

我将着陆以下内容:

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

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


因为PUT、GET和DELETE引用资源,所以它们在定义上也是幂等的。

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

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


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

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

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

在这一点上,有些人会争辩说,如果使用RESTful URL的,客户端确实知道资源的URL,因此PUT是可以接受的。毕竟,这就是为什么规范、标准化、Ruby on Rails、Django URL很重要,看看Twitter的API……等等等等。那些人需要理解没有这样的东西作为一个RESTful URLRoy Fielding自己说

REST API不得定义固定的资源名称或层次结构(一个客户端和服务器的明显耦合)。服务器必须有自由来控制自己的命名空间。相反,允许服务器指示客户端如何构建适当的URI,例如在超文本标记语言中完成表单和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),但他们还是这样做了,因为他们不知道如何解决这个问题。这种担忧在以下情况中表现出来:

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

第6步是人们通常对该做什么感到困惑的地方。然而,没有理由创建一个拼凑来解决这个问题。相反,HTTP可以按照rfc2616中的指定使用,服务器回复:

10.4.10 409冲突

由于与当前服务器冲突,请求无法完成资源的状态。此代码仅在以下情况下允许预计用户可能能够解决冲突并重新提交请求。响应正文应该包含足够的

用于用户识别冲突源的信息。理想情况下,响应实体将包含足够的信息用户或用户代理来解决问题;但是,这可能不是可能的,不需要的

冲突最有可能发生在对PUT请求的响应中。对于例如,如果正在使用版本控制并且实体正在PUT包括对资源的更改,这些更改与早期(第三方)请求,服务器可能会使用409响应以指示它无法完成请求。在这种情况下,响应实体可能包含之间差异的列表由响应Content-Type定义的格式的两个版本。

回复状态码409冲突是正确的追索权,因为

  • 执行具有与系统中已有资源匹配的ID的数据POST是“与资源的当前状态冲突”。
  • 因为重要的部分是客户端了解服务器拥有资源并采取适当的行动。这是一种“期望用户能够解决冲突并重新提交请求的情况”。
  • 包含冲突ID的资源的URL和资源的适当先决条件的响应将提供“足够的信息供用户或用户代理解决问题”,这是RFC 2616的理想情况。

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

rfc7231旨在替代2616,在第4.3.3节中描述了POST的以下可能响应

如果处理POST的结果相当于表示现有资源,源服务器可以重定向用户代理通过发送303(查看其他)响应到该资源使用位置字段中现有资源的标识符。这具有为用户代理提供资源标识符的好处并通过一种更适合于共享缓存,但如果用户以额外请求为代价代理尚未缓存表示。

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

有关更多信息,请阅读此文章

这里有一个简单的规则:

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

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

帖子:使用它来创建新资源。这就像带有自动递增ID的INSERT(SQL语句)。在响应部分,它包含一个新的生成ID。

POST也用于更新记录。

放:使用它来创建一个新的资源,但是这里我知道身份密钥。这就像INSERT(SQL语句),我事先知道身份密钥。在响应部分,它什么也不发送。

PUT也用于更新资源

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

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

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

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

让我们在这里非常清楚和直接。如果您是使用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用于更新没有争论。

我希望这能帮助任何阅读亚马逊和Sun/Java网站链接的微软开发人员。

在实践中,POST在创建资源方面效果很好。新创建资源的URL应在位置响应标头中返回。PUT应用于完全更新资源。请理解,这些是设计RESTful API时的最佳实践。HTTP规范本身并没有限制使用PUT/POST,但对创建/更新资源有一些限制。看看总结了最佳实践的http://techoctave.com/c7/posts/71-twitter-rest-api-dissected

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

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/标识符由服务器返回
幂等,即如果你PUT资源两次,它没有效果。示例:想做多少次就做多少次,结果将是一样的。x=1;POST既不安全也不幂等。示例:x++;
工程作为具体工作抽象
如果您使用PUT创建或更新资源,然后再次进行相同的调用,则资源仍然存在,并且仍然具有与第一次调用相同的状态。发出两个相同的POST请求很可能会导致两个资源包含相同的信息。

类比:

  • PUT即取和它在哪里。
  • POST作为在帖子办公室发送邮件。

在此处输入图片描述

社交媒体/网络类比:

  • 社交媒体上的发布:当我们发布消息时,它会创建新帖子。
  • (即编辑)我们已经发布的消息。

不熟悉这个话题的读者会被关于你应该做了什么的无休止的讨论所震惊,以及相对缺乏经验教训。我想,REST比SOAP“首选”这一事实是一种高层次的经验学习,但我们一定是从那里进步的?这是2016年。Roy的论文是在2000年写的。我们开发了什么?有趣吗?容易集成吗?支持吗?它能应对智能手机和不稳定移动连接的崛起吗?

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

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

或者你可以这么做:将你的不安全请求视为短暂的单用户资源(我们称之为操作)。客户端在实质性资源上请求一个新的“操作”,该资源的POST为空。POST将仅用于此目的。一旦安全地拥有了新创建的操作的URI,客户端将不安全请求提交给操作URI,不是目标资源。解决操作和更新“真实”资源是API的正确工作,在这里与不可靠的网络解耦。

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

你会很快发现与Promise的相似之处:我们在做任何事情之前创建并返回结果的占位符。也像Promise一样,一个动作可以成功或失败一次,但它的结果可以重复获取。

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

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

连续的删除请求可以看到和处理原始确认,而不会出现404错误。如果时间比预期的要长,我们可以临时响应,并且我们有一个客户端可以查看最终结果的地方。这种模式最好的部分是它的Kung-ft(Panda)属性。我们采取了一个弱点,即客户端在任何时候不理解响应时重复请求的倾向,并将其变成一种力量:-)

在告诉我这不是RESTful之前,请考虑一下尊重REST原则的多种方式。客户端不构造URL。API保持可发现性,尽管在语义学上有一点变化。HTTP动词的使用得当。如果你认为这是一个巨大的实现变化,我可以从经验告诉你它不是。

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

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

  1. 选择
  2. 插入
  3. 更新
  4. 删除
  5. 合并(如果已存在则更新,否则插入)

我使用PUT进行合并和更新操作,并使用POST进行插入。

简而言之:

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

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

与数据库查询的类比

你可以想到类似于"UPDATE学生设置地址="abc"其中id="123";

POST你可以想到“INSERT INTO学生(姓名,地址)值(“abc”,“xyzzz”);

学生ID是自动生成的。

使用PUT时,如果同一查询被执行多次或一次,则学生表状态保持不变。

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

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

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

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

情况1:当你在时间轴上发布一些东西时,它是一个新的条目。所以在这种情况下,他们使用POST方法,因为POST方法是非幂等的。

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

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

在一行中,使用POST在数据库中添加一个新条目,在数据库中添加更新

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

  • POST一个资源到一个集合
  • 由集合/: id标识的资源

例如:

  • POST /items
  • /items/1234

在这两种情况下,请求正文都包含要创建或更新的资源的数据。从路由名称中应该很明显,POST不是幂等的(如果你调用它3次,它将创建3个对象),但PUT是幂等的(如果你调用它3次,结果是一样的)。PUT通常用于“up插入”操作(创建或更新),但如果你只想用它来修改,你总是可以返回404错误。

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

请记住,REST是一组保持API简单的约定和指南。如果你最终得到一个复杂的解决方法只是为了选中“RESTful”框,那么你就失去了目的;)

简短回答:

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

长答案:

帖子:

  • POST用于将数据发送到服务器。
  • 当资源的URL为未知

放:

  • PUT用于将状态传输到服务器
  • 当资源的URL已知时有用

更长的答案:

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

从REST架构的角度来看,没有什么是重要的。我们也可以没有PUT生活。但从客户端开发人员的角度来看,它使他/她的生活变得简单得多。

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

那么,应该使用哪一个来创建资源?还是需要同时支持两者?

您应该使用PATCH。您可以修补问题列表,例如

PATCH /questions HTTP/1.1

使用包含要创建的对象的列表,例如

[{"title": "I said semantics!","content": "Is this serious?","answer": "Not really"}]

这是一个PATCH请求

  • 您修改现有资源列表而不提供全新内容
  • 您将新问题的状态从不存在更改为现有而不提供所有数据(服务器很可能会添加id)。

此方法的一大优点是您可以使用单个请求创建多个实体,只需在列表中提供它们即可。

这显然是PUT做不到的。您可以使用POST创建多个实体,因为它是HTTP的厨房水槽,基本上可以做任何事情。

缺点是可能没有人这样使用PATCH。恐怕,我刚刚发明了它,但我希望,我提供了一个很好的论证。

您可以使用CREATE代替,因为允许自定义HTTP动词,只是它们可能无法与某些工具一起使用。

关于语义学,CREATE是唯一正确的选择,其他一切都是圆孔中的方钉。不幸的是,我们只有圆孔。

除了其他人提出的不同意见外,我还想补充一点。

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

方法中,您必须在x-www-form-urlencoded中发送主体参数

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

根据这一点,您不能在方法中发送文件或多部分数据

编辑

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

这意味着如果你必须提交

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

你应该使用POST方法

我认为还有一个有趣的观点没有在这个PUT vs POST问题上分享:

如果你想有一个没有JavaScript的Web应用程序(例如,如果有人使用命令行浏览器,如Lynx或浏览器插件,如NoScript或uMatrix),你将不得不使用POST发送数据,因为超文本标记语言表单只支持GET和POST HTTP请求。

基本上,如果您想使用渐进式增强(https://en.wikipedia.org/wiki/Progressive_enhancement)使您的Web应用程序在任何地方都可以工作,无论是否使用JavaScript,您都不能使用其他HTTP方法,例如PUT或DELETE,这些方法仅在HTTP版本1.1中添加。

除了上面的所有答案:


最常用于专业实践,


  • 我们在创建操作中使用PUT over POST。为什么?因为这里的许多人也说,响应不可缓存,而POST响应可缓存(需要内容位置和过期)。
  • 我们在更新操作中使用POST而不是PUT。为什么?因为它使整个包含资源的缓存副本无效。这在更新资源时很有帮助。

总结

  • 使用PUT目标资源的状态创建或替换为请求中包含的表示所定义的状态。标准化的预期效果是幂等,因此它通知中介,他们可以在通信失败时重复请求。
  • 否则使用POST(包括创建或替换目标资源以外的资源的状态)。它的预期效果是不标准化,因此中介不能依赖任何通用属性。

参考文献

rfc7231给出了POSTPUT请求方法之间语义差异的最新权威描述(Roy Fielding,Julian Reschke,2014):

封闭表示的不同意图突出了POSTPUT方法之间的根本区别。POST请求中的目标资源旨在根据资源自己的语义学处理封闭表示,而PUT请求中的封闭表示被定义为替换目标资源的状态。因此,PUT的意图是幂等的,对中介可见,即使确切的效果只有源服务器知道。

换句话说,PUT的预期效果是POST0(用请求中包含的表示定义的状态创建或替换POST1资源的状态),因此对所有目标资源都是通用的,而POST的预期效果是POST2,因此特定于每个目标资源。因此POST可以用于任何东西,包括用于实现PUT的预期效果和其他请求方法(GETHEADDELETECONNECTOPTIONSTRACE)。

但建议在适用时始终使用更专业的请求方法而不是POST,因为它为中介提供了更多信息,用于自动信息检索(因为GETHEADOPTIONSTRACE定义为GET3),处理通信故障(因为GETHEADPUTDELETEOPTIONSTRACE定义为GET4),并优化缓存性能(因为GETHEAD定义为GET5),如GET6(Roy Fielding,2009)所述:

只有在其他方法非常适合的情况下,POST才会成为一个问题:例如,检索应该是某个资源表示的信息(GET),完全替换表示(PUT),或者任何其他标准化方法告诉中介比“这可能会改变一些东西”更有价值的东西。其他方法对中介更有价值,因为它们说明了如何自动处理故障以及中间缓存如何优化它们的行为。POST没有这些特征,但这并不意味着我们可以没有它。POST在HTTP中有许多有用的目的,包括“这个动作不值得标准化”的一般目的。

对我来说,理解差异的关键是理解资源的谁定义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|                                 |

下面有很多很好的答案和细节,但这帮助我找到了重点。

上面和下面的所有答案都是正确的,只是一个小(重要)注释。所有这些“动词”都是建议,它们的效果并不强制执行。服务器可以自由地做他们想做的任何事情,这意味着使用GET或服务器想要的任何东西编写。这一切都取决于实现后端。

例如,PHP读取$_POST$_GET。通过从这些数组中读取变量来完成什么完全取决于程序员。

POST用于将数据发送到服务器。
PUT用于将数据存入服务器上的资源(例如文件)。

我在书HTTP:权威指南的脚注(第55页)中看到了这一点。

用最简单的解释方式:

POST做它所说的,POST意味着它呈现创建新对象的请求。MDN将此称为“其他副作用”,一个例子是递增索引(“POST”这个词意味着什么)。

可以被认为是更新存量数据对象,当人们说它可以用于添加项目时。这是因为它可以从现有的父对象更新子null值。

MDN方法PUT文档

从表面上看,POST和PUT是一样的,但也有一些不同之处。

在HTTP Essentials:安全、可扩展网站的协议中,作者说:

POST和PUT的区别在于服务器如何解释统一资源标识符。使用POST,uri标识一个服务器上可以处理包含数据的对象。使用PUT,另一方面,uri标识服务器所在的对象应该放置数据。虽然POST uri通常表示程序或者脚本,PUT uri通常是文件的路径和名称。

作者建议我们使用PUT来上传文件,而不是POST。POST用于提交表单。

如果实现正确,GET、HEAD、PUT和DELETE方法是幂等的,但POST方法不是。因此,当你做两个PUT时——你会得到一条新记录,当你做两个POST时——你会得到两条新记录。

请注意,超文本标记语言表单仅支持GET和POST作为HTTP请求方法

<form method="put">是无效的超文本标记语言,将被视为,即发送GET请求。