get post put delete 区别_问题回答:Http 请求的Post 和Put 的区别

667388534820db526717cb2b919ffc39.png

609bbe407a5e1b8129feba756e8e4296.png

这是在我们的测试进阶社群的微信讨论群里有人讨论的一个面试题:

Http 请求的 post 和 put的区别是什么?

d527c29836da5e72e129a2f9eb395318.png

很快有人回答“put请求单个数据”,后面提问人又去网上查了资料,查出来 put 和 post的区别是 “put 同样的请求,前一条会被后一条覆盖”。

a4728db512e1cd5f8945c8afd440feab.png

接着另一位群友又发表了不同意见,并直接在群里上传了一个截图:

c63495a988c8151979e3fa9664faccbe.png
群友不知道哪里找来的通俗解释

大家对这个问题看法各异,查到的资料也都不一样。那么到底Http 请求的 post 和 put的区别是什么呢?特别提一下,我们不能把一些中文资料里的通俗解释当作标准答案。在网上查资料,我们需要培养一定的鉴别能力。

首先,这里有一个误区,很多没写过 web 应用的同学认为这两个方法的功能区别是固有的,一定存在的。但实际上, http 请求的 post 和 put分别具体实现什么样的功能,都是由程序员在写服务端代码时决定的。一个 post 请求和一个 put 请求上携带的信息量是一样的,同样的 http 请求头(header),同样的 http 请求体(body),唯一不同的是请求方法名,一个叫 post,一个叫 put。理论上来说程序员完全可以实现功能一样的处理函数来处理 post 请求和 put请求。

例1:程序员其实完全可以实现相同的 post 和 put请求的处理函数,以下使用的是hug框架。

import 

但是,既然携带的信息量相同,具体功能也是程序员自行决定,为什么 http 协议里要有 post 方法 和 put 方法两个方法呢?原来,在 http协议里,对 post 和 put是有官方建议的使用方法的(其实对所有http请求方法都有官方建议用法。)因特网的网络协议是在RFC里定义的,在网上,我们可以查到定义http协议的RFC。在w3c的网站上,我们可以找到HTTP协议相关的RFC:https://www.w3.org/Protocols

最早的是1999年的 RFC2616和 RFC2617,其中RFC2616里9.6节专门写了 Post 和 Put 的区别。

6d4ca17ad079a74060eb58b6b46d89ad.png

以下是RFC2616在网络上流传的中文版中对 Post 和 Put的区别的描述:

POST方法和PUT方法请求最根本的区别是请求URI(Request-URI)的含义不同。POST请求里的URI指示一个能处理请求实体的资源(译注:此资源可能是一段程序,如jsp里的servlet) 。此资源可能是一个数据接收过程,一个网关(gateway,译注:网关和代理服务器的区别是:网关可以进行协议转换,而代理服务器不能,只是起代理的作用,比如缓存服务器其实就是一个代理服务器),或者一个单独接收注释的实体。而PUT方法请求里有一个实体一一用户代理知道URI意指什么,并且服务器不能把此请求应用于其他URI指定的资源。如果服务器期望请求被应用于一个不同的URI,那么它必须发送301(永久移动了)响应;用户代理可以自己决定是否重定向请求。

但之后RFC 2616在2014年被废弃,改为 RFC 723X系列。

48aa4ddf0fc86edafd3f84d2936b85d0.png

在RFC 7231 (https://tools.ietf.org/html/rfc7231)里4.3.3 Post 和4.3.4 Put 分别讲解了这两个方法。其中,也有对 Post 和 Put 区别的官方描述:但是很遗憾,我暂时搜不到RFC 7231中文翻译版。以下只能是我个人尝试按照自己理解翻译一下了,翻译水平有限,建议大家还是看原版。


POST方法和PUT方法请求最根本的区别是发起请求的目的不同。post 请求的目的是根据资源自身的语义来处理这个资源(译注:我理解这个意思就是说,post请求可以根据实际请求的资源来决定到底怎么处理,原文中4.3.3节给了一些例子。我会把我的翻译版附在本文末尾。)。put请求的目的是用来替换整个目标资源。put 请求具有 幂等性(idempotent)。(幂等性的意思在这个RFC的4.2.2里说了“同样的请求,不管发多少次,每次服务器处理完之后的结果,都和只发一次是一样的。”这里我举个例子帮助大家理解:你的服务器端存放了一个文章收藏夹,你通过一个put请求来修改收藏夹里的文章名字,这个 put 请求的请求体里包含了整个文章收藏夹里所有文章的名字。当服务端处理这个请求时,只要从 put 请求里拿出这个文章名字列表,整个存到服务器上,就是一个完整的新的收藏夹。那么这样的put 请求,无论你是发一次,还是发多次,假设服务器都能成功处理,那么最终对服务器造成的影响都是一样的:即服务器端原有的收藏夹里的文章列表被替换成了put请求里带的收藏夹的文章列表。)

除了幂等性(idempotent),官方文档里还有其他的一些详细介绍,比如缓存性,post 是可以被缓存的,put 却不行。RFC文档里put 请求的介绍特别长。我猜你如果在面试时能跟面试官讲一讲幂等性之类的http方法的特性,再挑剔的面试官也会满意的吧。而除了这个官方给的最根本区别以外,我们还可以结合实际工作经验中用到的 post 和 put 来谈一谈这两者的区别。毕竟,官方只是提供了这两个请求方法,然后要求大家去用,但程序员真实使用的web 框架未必要求大家百分百按照 http协议来对请求做处理。

那如果你做的项目里根本没用到这些方法,或者你们的开发把服务端逻辑写得很有个性没按协要求来。。。 那可以试试来参加我们的项目实战,这次的项目实战里用的 Github api 可是会用到大多数 http 请求方法的。而且 Github 的http 接口本身实现也比较规范,属于比较有参考价值的。

张挺:测试进阶社群项目实战启动​zhuanlan.zhihu.com
63a52ea6fc2e5def6fe22bf5cd6fc883.png

最后附上我翻译的一个半的RFC7231的小节(post的翻完了,put的翻了一小半。。实在太长,需要的可以阅读原文):


4.3.3 post

服务端在处理时post方法发出的请求时,应该按照请求的具体资源的具体语义来对该资源做相应的处理。例如,以下情况一般使用 Post 方法:

  • 提供一整块数据时,比如,向html表单的文本框里输入数据,然后把整个表单提交,来对这些数据做处理。
  • 在论坛、群组、邮件列表、博客或者类似的东西里发表文章时。
  • 想要通过请求在服务器端创建一个新资源时。
  • 像服务端的一个资源里插入一些数据时。

服务端根据这些数据处理的实际结果来选择一个Http响应状态码。除了206 (Partial Content), 304 (Not Modified), and 416 (Range Not Satisfiable) 这三个以外的 http响应状态码都可以作为 Post 请求的响应状态码。

如果post请求希望创建一个或多个资源,并且服务端把这些资源成功创建了,那么应该(原文大写的SHOULD)返回一个 201 (Created) 并且在响应头(response header)里加入Location字段,值为创建出来的资源的id。并返回“a representation that describes the status of the request while referring to the new resource(s)” (译注:这一段每个词我都认识,但连起来我就看不懂了。。。)

Post 请求的响应在响应里明确包含了freshness信息时,这个响应是可缓存的。然而 Post 请求的缓存机制并没有被广泛实现和应用。假如服务端真的希望客户端能够利用缓存机制来缓存一个post请求的响应里的信息(也就是这个post创建出来的资源的URI),并且在后面的get 请求里重用这些被缓存的信息,那么服务端可以返回一个 状态码为200(OK)的响应,并且在响应头里包含 Content-location这个字段,其值为 post请求创建的资源URI。

如果post请求希望创建一个或多个资源,但服务端发现这些资源本来就存在,服务端可以回复一个303 (See Other),表示希望客户端进行重定向。同时在响应头里加入Location字段,值为现有的这个资源的id。

4.3.4 put

put方法用于创建或者替换整个资源,请求体(body)里包含这个资源的完整描述。一个 put 请求如果成功了,那么理论上来说,对同样的 url 做一个 get 请求,应该能得到一个 200 (OK) 的响应。但实际上,并不能保证get 到200的响应。因为,可能同时有其他人也在对这个资源做操作,那么就可能在get请求抵达服务器之前,这个资源已经被别人改掉了。所以说, put 请求成功,只能代表在这个请求被服务器处理的那个时刻上,这个资源被创建或替换成请求体里的样子了。(译注:比如我用put请求修改一个小组里的所有用户列表,我的请求体里包含了小组里所有用户的用户名列表,那么在服务器处理我这个请求,并告诉我成功了的那个时刻,这个小组的用户名一定和我发的put请求里的用户名列表是一样的。但是同时有可能有别的人也在尝试改这个小组的信息,那么我紧接着发一个 get 请求,可能get到的小组里的用户名列表跟我刚才put的已经不一样了。)

如果put请求希望创建一个或多个资源,并且服务端把这资源成功创建了,那么服务端必须(原文大写的MUST)给用户返回一个状态码为 201 (Created) 的响应。而如果这个请求是用来修改某个资源的状态的,那么服务端 必须(MUST)返回一个状态码为 200 (OK) 的响应或者一个状态码为 204 (No Content)的响应。 200 和204 都表示成功完成了这个请求。

服务端应该(SHOULD)忽略 put请求头里无法识别出含义的字段,而不应该把header里的这些字段也存在资源里。

(关于 PUT的 描述太长了,后面还有整整两页没翻,以后有空接着翻译吧。。。或者哪位读者感兴趣可以翻一下。)


609bbe407a5e1b8129feba756e8e4296.png
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值