【计算机网络】面试常问之GET和POST的区别



HTTP1.0定义了三种请求方法:GET、POST、HEAD。

HTTP最早被用来做浏览器与服务器之间交互HTML和表单的通讯协议
后来又被广泛地扩充到接口格式的定义上。
所以在讨论GET和POST区别的时候,需要先确定到底是浏览器使用的GET/POST,还是用HTTP作为接口传输协议的场景。

1)浏览器的GET和POST

这里特指浏览器中非Ajax的HTTP请求,即从HTML和浏览器诞生就一直使用的HTTP协议中的GET/POST。

  • 浏览器用GET请求来获取一个html页面/图片/css/js等资源。
  • 浏览器用POST来提交一个表单,并得到一个结果的网页。

1.1 幂等(Idempotent)上的区别

幂等的概念是指同一个请求方法执行多次和仅执行一次的效果完全相同

  • 引入幂等主要是为了处理同一个请求重复发送的情况,比如在请求响应前失去连接,如果方法是幂等的,就可以放心地重发一次请求。
  • GET:是“读取“一个资源,比如Get到一个html文件。反复读取不应该对访问的数据有副作用
    • 因为GET是读取,就可以对GET请求的数据做缓存。这个缓存可以做到浏览器本身上(彻底避免浏览器发请求),也可以做到代理上(如nginx),或者做到server端(用Etag,至少可以减少带宽消耗)。
  • POST:在页面里 标签会定义一个表单,点击其中的submit元素会发出一个POST请求让服务器做一件事。这件事往往是有副作用的,不幂等的,即重复请求可能会带来意想不到的后果。(如果尝试重新执行POST请求,浏览器也会弹一个框提示下这个刷新可能会有副作用,询问要不要继续。)
    • 不幂等也就意味着不能随意多次执行,因此也就不能缓存。比如通过POST下一个单,服务器创建了新的订单,然后返回订单成功的界面,这个页面是不能被缓存。
    • 因为POST可能有副作用,所以浏览器不能把POST请求保存为书签

1.2 携带数据的格式上的区别

一般会泛泛地说:GET请求没有body,只有url,请求数据放在url的querystring中;POST请求的数据在body中。但这种情况仅限于浏览器发请求的场景。

  • 当浏览器发出一个GET请求时,就意味着要么是用户自己在浏览器的地址栏输入,要不就是点击了html里a标签的href中的url。所以其实并不是GET只能用url,而是浏览器直接发出的GET只能由一个url触发

    • 所以如果GET上要在url之外带一些参数,就只能依靠url上附带querystring。
  • 浏览器的POST请求都来自表单提交。每次提交,表单的数据被浏览器编码到HTTP请求的body里。浏览器发出的POST请求的body主要有有两种格式

    • 一种是application/x-www-form-urlencoded用来传输简单的数据,大概就是"key1=value1&key2=value2"这样的格式。
    • 另外一种是传文件,会采用multipart/form-data格式。采用后者是因为application/x-www-form-urlencoded的编码方式对于文件这种二进制的数据非常低效。

  • 浏览器在POST一个表单时,url上也可以带参数,只要<form action=“url” >里的url带querystring就行。只不过表单里面的那些用 等标签经过用户操作产生的数据都在会在body里。


2)接口中的GET和POST

这里是指通过浏览器的Ajax api,或者iOS/Android的App的http client,java的commons-httpclient/okhttp或者是curl,postman之类的工具发出来的GET和POST请求

  • 此时GET/POST不光能用在前端和后端的交互中,还能用在后端各个子服务的调用中(即当一种RPC协议使用)。尽管RPC有很多协议,比如thrift,grpc,但是http本身已经有大量的现成的支持工具可以使用,并且对人类很友好,容易debug。HTTP协议在微服务中的使用是相当普遍的。

当用HTTP实现接口发送请求时,就没有浏览器中那么多限制了,只要是符合HTTP格式的就可以发。HTTP请求的格式,大概是这样的一个字符串(为了美观,在\r\n后都换行一下):

<METHOD> <URL> HTTP/1.1\r\n
<Header1>: <HeaderValue1>\r\n
<Header2>: <HeaderValue2>\r\n
...
<HeaderN>: <HeaderValueN>\r\n
\r\n
<Body Data....>

其中的<METHOD>可以是GET也可以是POST,或者其他的HTTP Method,如PUT、DELETE、OPTION……

  • 从协议本身看,并没有什么限制说GET一定不能没有body,POST就一定不能把参放到<URL>的querystring上。因此其实可以更加自由的去利用格式。可以做各种各样的定制,只要请求的客户端和服务器端能够约定好

常见的接口规范/风格如REST,充分运用GET、POST、PUT和DELETE,约定了这4个接口分别获取、创建、替换和删除“资源”。

  • 这样仅仅通过看HTTP的method就可以明白接口是什么意思,并且解析格式也得到了统一。
  • REST最佳实践还推荐在请求体使用json格式。
    • json相对于x-www-form-urlencoded的优势在于:可以有嵌套结构,可以支持更丰富的数据类型。
    • 通过一些框架,json可以直接被服务器代码映射为业务实体。用起来十分方便。
    • 但是如果是写一个接口支持上传文件,那么还是multipart/form-data格式更合适。

REST中GET和POST不是随便用的。

  • 在REST中, 【GET】 + 【资源定位符】被专用于获取资源或者资源列表。
  • 【POST】+ 【资源定位符】则用于创建一个资源。

3)安全性

常听到GET不如POST安全,因为POST用body传输数据,而GET用url传输,更加容易看到。

  • 但是从攻击的角度,无论是GET还是POST都不够安全,因为HTTP本身是明文协议。每个HTTP请求和返回的每个byte都会在网络上明文传播,不管是url,header还是body。

为了避免传输中数据被窃取,必须做从客户端到服务器的端端加密。业界的通行做法就是https——即用SSL协议协商出的密钥加密明文的http数据。这个加密的协议和HTTP协议本身相互独立。如果是利用HTTP开发公网的站点/App,要保证安全,https是最最基本的要求。

回到HTTP本身,GET请求的参数更倾向于放在url上,因此有更多机会被泄漏

  • 比如携带私密信息的url会展示在地址栏上,还可以分享给第三方,就非常不安全了。
  • 此外,从客户端到服务器端,有大量的中间节点,包括网关,代理等。他们的access log通常会输出完整的url,比如nginx的默认access log就是如此。如果url上携带敏感数据,就会被记录下来。
  • 但请注意,就算私密数据在body里,也是可以被记录下来的,因此如果请求要经过不信任的公网,避免泄密的唯一手段就是https

如果是用作接口,GET实际上也可以带body,POST也可以在url上携带数据。所以实际上到底怎么传输私密数据,要看具体场景具体分析。当然,绝大多数场景,用POST + body里写私密数据是合理的选择


4)编码

常见的说法有,比如GET的参数只能支持ASCII,而POST能支持任意binary,包括中文。但其实从上面可以看到,GET和POST实际上都能用url和body。因此所谓编码确切地说应该是http中url用什么编码,body用什么编码

url用什么编码?

  • url只能支持ASCII的说法源自于RFC1738,即ASCII的子集a-zA-Z0-9$-_.+!*'(),是可以“不经过编码”在url中使用。比如尽管空格也是ASCII字符,但是不能直接用在url里。
  • 一种叫做percent encoding的编码方法可以把字符转换成URL可用字符,因此即使是binary data,也可以通过编码后放在URL上的。
    • 注意:这个编码方式只管把字符转换成URL可用字符,但是却不管字符集编码(比如中文到底是用UTF8还是GBK),这块早期一直都相当乱,也没有什么统一规范。
    • 比如有时跟网页编码一样,有的是操作系统的编码一样。最要命的是浏览器的地址栏是不受开发者控制的。这样,对于同样一个带中文的url,如果有的浏览器一定要用GBK(比如老的IE8),有的一定要用UTF8(比如chrome)。后端就可能认不出来。
    • 对此常用的办法是避免让用户输入这种带中文的url。如果有这种形式的请求,都改成用户界面上输入,然后通过Ajax发出的办法。Ajax发出的编码形式开发者是可以100%控制的。

不过目前基本上utf8已经大一统了。现在的开发者除非是被国家规定要求一定要用GB系列编码的场景,基本上不会再遇到这类问题了。

  • 顺便说一句,尽管在浏览器地址栏可以看到中文。但这种url在发送请求过程中,浏览器会把中文用字符编码+Percent Encode翻译为真正的url,再发给服务器。浏览器地址栏里的中文只是想让用户体验好些而已。

body用什么编码?

HTTP Body相对好些,因为有个Content-Type来比较明确的定义。比如:

POST xxxxxx HTTP/1.1
...
Content-Type: application/x-www-form-urlencoded ; charset=UTF-8
  • 这里Content-Type会同时定义请求body的格式(application/x-www-form-urlencoded)和字符编码(UTF-8)。
  • 浏览器直接发出的POST请求就是表单提交,而表单提交只有application/x-www-form-urlencoded针对简单的key-value场景;和multipart/form-data,针对只有文件提交,或者同时有文件和key-value的混合提交表单的场景。
  • 如果是Ajax或者其他HTTP Client发出去的POST请求,其body格式就非常自由了,常用的有json,xml,文本,csv……甚至是你自己发明的格式。只要前后端能约定好即可。

所以body和url都可以提交中文数据给后端,但是POST的规范好一些,相对不容易出错,容易让开发者安心。对于GET+url的情况,只要不涉及到在老旧浏览器的地址栏输入url,也不会有什么太大的问题。

5)浏览器的POST需要发两个请求吗?

使用HTTP时大家会有一个约定,即所有的“控制类”信息应该放在请求头中,具体的数据放在请求体里“。于是服务器端在解析时,总是会先完全解析全部的请求头部。这样,服务器端总是希望能够了解请求的控制信息后,就能决定这个请求怎么进一步处理,是拒绝,还是根据content-type去调用相应的解析器处理数据,或者直接用zero
copy转发。

常说GET产生一个TCP数据包,POST产生两个TCP数据包。意思是:

  • 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据)。
  • 而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
  • 因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。

BUT,其实上述有些坑,需要注意的是:

  • GET与POST都有自己的语义,不能随便混用。
  • 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。
  • 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。


【部分内容参考自】

  • GET 和 POST 到底有什么区别?:https://www.zhihu.com/question/28586791/answer/767316172
  • http 之get和post的区别 --面试:https://www.cnblogs.com/corgisyj/p/12879165.html
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值