99%的人都理解错了 HTTP 中 GET 与 POST 的区别

GET和POST是HTTP请求的两种基本方法,要说它们的区别,接触过WEB开发的人都能说出一二。
最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数。
你可能自己写过无数个GET和POST请求,或者已经看过很多权威网站总结出的他们的区别,你非常清楚知道什么时候该用什么。
当你在面试中被问到这个问题,你的内心充满了自信和喜悦。

你轻轻松松的给出了一个“标准答案”:

GET在浏览器回退时是无害的,而POST会再次提交请求。
GET产生的URL地址可以被Bookmark,而POST不可以。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求在URL中传送的参数是有长度限制的,而POST么有。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
GET参数通过URL传递,POST放在Request body中。

“很遗憾,这不是我们要的回答!”

如果我告诉你GET和POST本质上没有区别你信吗?
让我们扒下GET和POST的外衣,坦诚相见吧!

GET和POST是什么?HTTP协议中的两种发送请求的方法。

HTTP是什么?HTTP是基于TCP/IP的关于数据如何在万维网中如何通信的协议。

HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP链接。GET和POST能做的事情是一样一样的。你要给GET加上request body,给POST带上url参数,技术上是完全行的通的。

“标准答案”里的那些区别是怎么回事?


在我大万维网世界中,TCP就像汽车,我们用TCP来运输数据,它很可靠,从来不会发生丢件少件的现象。但是如果路上跑的全是看起来一模一样的汽车,那这个世界看起来是一团混乱,送急件的汽车可能被前面满载货物的汽车拦堵在路上,整个交通系统一定会瘫痪。为了避免这种情况发生,交通规则HTTP诞生了。HTTP给汽车运输设定了好几个服务类别,有GET, POST, PUT, DELETE等等,HTTP规定,当执行GET请求的时候,要给汽车贴上GET的标签(设置method为GET),而且要求把传送的数据放在车顶上(url中)以方便记录。如果是POST请求,就要在车上贴上POST的标签,并把货物放在车厢里。当然,你也可以在GET的时候往车厢内偷偷藏点货物,但是这是很不光彩;也可以在POST的时候在车顶上也放一些数据,让人觉得傻乎乎的。HTTP只是个行为准则,而TCP才是GET和POST怎么实现的基本。

但是,我们只看到HTTP对GET和POST参数的传送渠道(url还是requrest body)提出了要求。“标准答案”里关于参数大小的限制又是从哪来的呢?

在我大万维网世界中,还有另一个重要的角色:运输公司。不同的浏览器(发起http请求)和服务器(接受http请求)就是不同的运输公司。 虽然理论上,你可以在车顶上无限的堆货物(url中无限加参数)。但是运输公司可不傻,装货和卸货也是有很大成本的,他们会限制单次运输量来控制风险,数据量太大对浏览器和服务器都是很大负担。业界不成文的规定是,(大多数)浏览器通常都会限制url长度在2K个字节,而(大多数)服务器最多处理64K大小的url。超过的部分,恕不处理。如果你用GET服务,在request body偷偷藏了数据,不同服务器的处理方式也是不同的,有些服务器会帮你卸货,读出数据,有些服务器直接忽略,所以,虽然GET可以带request body,也不能保证一定能被接收到哦。

好了,现在你知道,GET和POST本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。

你以为本文就这么结束了?

我们的大BOSS还等着出场呢。。。

这位BOSS有多神秘?当你试图在网上找“GET和POST的区别”的时候,那些你会看到的搜索结果里,从没有提到他。他究竟是什么呢。。。

GET和POST还有一个重大区别,简单的说:

GET产生一个TCP数据包;POST产生两个TCP数据包。

长的说:

对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);

而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

也就是说,GET只需要汽车跑一趟就把货送到了,而POST得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。

因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。因此Yahoo团队有推荐用GET替换POST来优化网站性能。但这是一个坑!跳入需谨慎。为什么?

1. GET与POST都有自己的语义,不能随便混用。

2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。

3. 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。

现在,当面试官再问你“GET与POST的区别”的时候,你的内心是不是这样的?

php
本作品采用《CC 协议》,转载必须注明作者和本文链接
不要轻易放弃。学习成长的路上,我们长路漫漫,只因学无止境 Don't give up easily. On the way of learning and growing up, we have a long way to go, just because there is no end to learning.
本帖由系统于 5年前 自动加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 23

嗯.. 貌似没讲到实际应用中的重点. 100 Continue 的规范可以参照 MND https://developer.mozilla.org/zh-CN/docs/W....

浏览器有没有默认发送 Expect:100-continue 的请求头就不清楚, 但是 curl 是默认会发送的. 具体的解释可以看鸟哥的博客: http://www.laruence.com/2011/01/20/1840.ht...

摘抄一下重点:

在使用curl做POST的时候, 当要POST的数据大于1024字节的时候, curl并不会直接就发起POST请求, 而是会分为俩步,

 1. 发送一个请求, 包含一个Expect:100-continue, 询问Server使用愿意接受数据
 2. 接收到Server返回的100-continue应答以后, 才把数据POST给Server

并不是所有的Server都会正确应答100-continue, 比如lighttpd, 就会返回417 “Expectation Failed”, 则会造成逻辑出错.

因此, 在使用 curl 时可以考虑增加一步处理, 既在请求头中将 Expect 值置空. 鸟哥给出的 demo 是这样

curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));

guzzle 的做法是:

// If the Expect header is not present, prevent curl from adding it
if (!$request->hasHeader('Expect')) {
    $conf[CURLOPT_HTTPHEADER][] = 'Expect:';
}

补充一下, 实在是感觉这篇鬼东西都不知道在说啥. 我认为 GET 跟 POST 跟区别就是前者没有 request body. 同样看一下 MDN

https://developer.mozilla.org/zh-CN/docs/W...
https://developer.mozilla.org/zh-CN/docs/W...
https://developer.mozilla.org/zh-CN/docs/W...

file

但实际上, GET 也是可以带 request body 的, elasticsearch 的 HTTP API 就允许使用 GET 方法提交 request body
https://www.elastic.co/guide/en/elasticsea...

Both HTTP GET and HTTP POST can be used to execute search with body. Since not all clients support GET with body, POST is allowed as well.

同时, 也在 StackOverflow 搜到了关于 GET 方法关于 request body 的 RFC 定义
https://stackoverflow.com/questions/369397...

A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.

Finally, note that while HTTP allows GET requests to have a body syntactically, this is done only to allow parsers to be generic; as per RFC7231, Section 4.3.1, a body on a GET has no meaning, and will be either ignored or rejected by generic HTTP software.

靠着谷歌翻译大概阅读了下, 意思就是 client 在使用 GET 的时候可以加上 request body, 但要不要接受或者使用是 server 决定的. 不同的 server 不同的实现.

5年前

666,长见识了!

5年前

666,长见识了!

5年前
guanhui07

chrome也是发送一次吧

5年前

结尾想笑😂😂😂

5年前

嗯.. 貌似没讲到实际应用中的重点. 100 Continue 的规范可以参照 MND https://developer.mozilla.org/zh-CN/docs/W....

浏览器有没有默认发送 Expect:100-continue 的请求头就不清楚, 但是 curl 是默认会发送的. 具体的解释可以看鸟哥的博客: http://www.laruence.com/2011/01/20/1840.ht...

摘抄一下重点:

在使用curl做POST的时候, 当要POST的数据大于1024字节的时候, curl并不会直接就发起POST请求, 而是会分为俩步,

 1. 发送一个请求, 包含一个Expect:100-continue, 询问Server使用愿意接受数据
 2. 接收到Server返回的100-continue应答以后, 才把数据POST给Server

并不是所有的Server都会正确应答100-continue, 比如lighttpd, 就会返回417 “Expectation Failed”, 则会造成逻辑出错.

因此, 在使用 curl 时可以考虑增加一步处理, 既在请求头中将 Expect 值置空. 鸟哥给出的 demo 是这样

curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));

guzzle 的做法是:

// If the Expect header is not present, prevent curl from adding it
if (!$request->hasHeader('Expect')) {
    $conf[CURLOPT_HTTPHEADER][] = 'Expect:';
}

补充一下, 实在是感觉这篇鬼东西都不知道在说啥. 我认为 GET 跟 POST 跟区别就是前者没有 request body. 同样看一下 MDN

https://developer.mozilla.org/zh-CN/docs/W...
https://developer.mozilla.org/zh-CN/docs/W...
https://developer.mozilla.org/zh-CN/docs/W...

file

但实际上, GET 也是可以带 request body 的, elasticsearch 的 HTTP API 就允许使用 GET 方法提交 request body
https://www.elastic.co/guide/en/elasticsea...

Both HTTP GET and HTTP POST can be used to execute search with body. Since not all clients support GET with body, POST is allowed as well.

同时, 也在 StackOverflow 搜到了关于 GET 方法关于 request body 的 RFC 定义
https://stackoverflow.com/questions/369397...

A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.

Finally, note that while HTTP allows GET requests to have a body syntactically, this is done only to allow parsers to be generic; as per RFC7231, Section 4.3.1, a body on a GET has no meaning, and will be either ignored or rejected by generic HTTP software.

靠着谷歌翻译大概阅读了下, 意思就是 client 在使用 GET 的时候可以加上 request body, 但要不要接受或者使用是 server 决定的. 不同的 server 不同的实现.

5年前
Oraoto

两个TCP数据包和两个HTTP请求还是不同的。

5年前

发送两个数据包是浏览器或者第三方工具的行为吧,和http协议的post方法有关系吗

5年前
lmaster

关于 100 Continue 这个问题 可以看下这个

file
大体意思:允许发送请求消息的客户机在客户机发送请求体之前确定源服务器是否愿意接受请求,但是有些服务器会拒绝。
也就说,能不能发和发不发由你,接不接由我,没有强制要求

而 HTTP 协议中 关于 GET 和 POST 其实也只做了语义上的说明,其他都是浏览器厂商自己实现上自己设定的。

5年前

那么问题来了,POST 和 PUT 的区别只有这个吗?
POST 往往是用来创建一个资源的,而 PUT 往往是用来修改一个资源的。

5年前

好棒

5年前
lmaster

@Ali 感觉你这个想是在说 REST 的,那 post 和 put 还有等幂的区分

5年前

兄弟:标准答案里边说的错了吗?只要回答 YES or NO。如果没错,你的标题就起错了。

5年前 评论
MiaMia 4年前
Haven

@Ali
摘自百度,大致可以理解为什么POST一般做创建,而PUT一般用来修改:
在HTTP中,PUT被定义为idempotent的方法,POST则不是,这是一个很重要的区别。
“Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.”
上面的话就是说,如果一个方法重复执行多次,产生的效果是一样的,那就是idempotent的。
也许你会觉得这个两个方法的差别没什么大不了的,用错了也不会有什么问题,但是你的服务一放到internet上,如果不遵从HTTP协议的规范,就可能给自己带来麻烦。比如,没准Google Crawler也会访问你的服务,如果让一个不是indempotent的服务可以用indempotent的方法访问,那么你服务器的状态可能就会被Crawler修改,这是不应该发生的。
举一个简单的例子,假如有一个博客系统提供一个Web API,模式是这样/blog/post/blog-name},很简单,将{blog-name}替换为我们的blog名字,往这个URI发送一个HTTP PUT或者POST请求,HTTP的body部分就是博文,这是一个很简单的REST API例子。我们应该用PUT方法还是POST方法?取决于这个REST服务的行为是否是idempotent的,假如我们发送两个/blog/post/Sample请求,服务器端是什么样的行为?如果产生了两个博客帖子,那就说明这个服务不是idempotent的,因为多次使用产生了副作用了嘛;如果后一个请求把第一个请求覆盖掉了,那这个服务就是idempotent的。前一种情况,每次返回结果不一样的时候,应该使用POST方法,后一种情况,应该使用PUT方法。

5年前

单独看这个问题很多答案是没有区分协议与实现造成的,但都谈不上说是谁对谁错,毕竟问题出现时是有前提环境的。

对于 HTTP 协议 RFC 中的描述,可以说 POST / GET 这些仅仅是在语义上有区别,实质没有区别,RFC 中对 GET 实现的描述并没有说 “必须” 不支持 BODY,也没有说 POST 请求在传输层 “必须” 是一次或两次 TCP 请求。对于 HTTP Client 的实现(包括但不仅是浏览器,还有一些请求库、命令工具等等),可能会是一个 HTTP 协议的超集,到实际情况就是各个浏览器的差异。

这个问题出现的前提是大多数时候都默认考察的是 Web 相关知识而不是 HTTP 协议本身。因此,首先是浏览器环境中差异,最常见的“ POST 请求比 GET 安全”来源就是 浏览器打开页面会自动发出 GET 请求。而另一个常见答案“GET 请求不会修改数据,幂等操作”是应用实现的建议,而非协议要求或强制要求。最直接的证据是仍然可以用 GET 请求修改数据——比如记录一个页面被 GET 请求的次数。

5年前
赵佳

长见识了。

5年前
赵佳

:+1: :+1: :+1:

5年前
赵佳

:+1: :+1: :+1: 学习了,长见识!

5年前
Complicated

学到了东西,结尾确实很搞笑

4年前
bestcyt

转载应该附上原文链接吧。。。

4年前

什么鬼,GET和POST请求区别啥时候变成这个了,POST请求在传输数据比较多的时候,确实可以分两步,以减少不必要网络传输,但是说这是GET和POST请求区别,事实上大部分现代浏览器都是一次传输,现在都以至于要把最后一个tls连接完成包、POST请求头和body放到一个IP包里了,抓个包看看就知道了,在各大库实现中,稍微正常一点的实现都是一次传输吧,包括系统curl shell命令也是这么实现的。

再说http协议自身并没有规定100 Continue不能用于其它类型请求吧,这也只是实现问题,所以没搞清楚就不要随便说,误人子弟太不好了。

4年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!