知识库

记录点点滴滴

GET与POST

前言

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

结论

从狭义的角度,即从传统浏览器GET/POST语义的角度考虑有以下区别

  • GET是幂等,对数据本身不会产生改变;POST是幂等的,往往会改变数据
  • GET可以缓存;POST往往不能缓存,刷新会有弹框
  • GET请求数据放在URL上;POST请求数据放在body中
  • GET语义上常用于获取数据;POST语义上常用于提交数据/让服务器做一件事

从广义角度考虑,即从接口应用角度考虑

  • GET/POST的实际用途取决于客户端与服务端之间的约定
  • GET请求body中可以存数,POST亦可以在URL中放数据,两者的规范可自行通过端到端约定
  • GET/POST请求常采用REST协议,POST也可采用幂等的方式(防止重复提交)

其他特征

  • 浏览器角度考虑,POST数据类型为application/x-www-form-urlencoded或者multipart/form-data两种类型,而接口角度考虑,POST的数据类型可自行约定,常见的有json/csv等格式
  • 由于服务器log常常会对URL进行记录,因此从安全角度考虑常采用POST+Body的形式用于登录等
  • URL中仅允许ASCII,而body中可通过端到端的设定,自行选定

浏览器中GET/POST区别

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

浏览器对GET和POST预先约定了语义,即:

GET

“读取“一个资源。因此反复读取不应该对访问的数据有副作用(不会对数据本身产生任何影响)。如果没有副作用,被称为“幂等“(Idempotent)。

因为GET因为是读取,就可以对GET请求的数据做缓存。这个缓存可以做到浏览器本身上(彻底避免浏览器发请求),也可以做到代理上(如nginx),或者做到server端(用Etag,至少可以减少带宽消耗)

POST

在页面里<form> 标签会定义一个表单。点击其中的submit元素会发出一个POST请求让服务器做一件事。这件事往往是有副作用的(会改变存储的数据本身),不幂等的。

因为不幂等也就不能随意多次执行的。因此也就不能缓存。试想一下,如果POST请求被浏览器缓存了,那么下单请求就可以不向服务器发请求,而直接返回本地缓存的“下单成功界面”,却又没有真的在服务器下单。那是一件多么滑稽的事情。

因为POST可能有副作用,所以浏览器一般实现为也不能将其保存为书签。(想想,点一下书签就下一个单,是不是很恐怖?)。此外如果尝试“刷新“POST请求,浏览器也会弹一个框提示下这个刷新可能会有副作用,询问要不要继续。

《GET与POST》

注:如果将HTTP POST作为接口的形式,而不是本章中所提的浏览器请求的形式,那么在实际操作过程中就不会有这个弹框

请求的数据

GET和POST携带数据的格式也有区别。

当浏览器发出一个GET请求时,就意味着要么是用户自己在浏览器的地址栏输入,要不就是点击了html里a标签的href中的url。所以其实并不是GET只能用url,而是浏览器直接发出的GET只能由一个url触发。所以没办法,GET上要带一些参数就只能依靠url上附带querystring。但是HTTP本身并没有这个限制(这个限制源于浏览器对HTTP的使用特性)。

而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就行。只不过表单里面的那些用<input> 等标签经过用户操作产生的数据都在会在body里

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

接口请求中的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请求,其并没有限制GET与POST的规范。即从协议本身的角度考虑,GET中可以有body,POST也可以将参数放在URL的querystring中,甚至可以放在header中,而这一切都取决于客户端和服务端之间的约定,可以任意DIY

当然,太自由也带来了另一种麻烦,开发人员不得不每次讨论确定参数是放url的path里,querystring里,body里,header里这种问题,太低效了。于是就有了一些列接口规范/风格。其中名气最大的当属REST。REST充分运用GET、POST、PUT和DELETE,约定了这4个接口分别获取、创建、替换和删除“资源”,REST最佳实践还推荐在请求体使用json格式。这样仅仅通过看HTTP的method就可以明白接口是什么意思,并且解析格式也得到了统一。
json相对于x-www-form-urlencoded的优势在于1)可以有嵌套结构;以及 2)可以支持更丰富的数据类型。通过一些框架,json可以直接被服务器代码映射为业务实体。用起来十分方便。但是如果是写一个接口支持上传文件,那么还是multipart/form-data格式更合适。
与浏览器的场景类似,REST GET也不应该有副作用,于是可以被反复无脑调用。浏览器(包括浏览器的Ajax请求)对于这种GET也可以实现缓存(如果服务器端提示了明确需要Caching);但是如果用非浏览器,有没有缓存完全看客户端的实现了。当然,也可以从整个App角度,也可以完全绕开浏览器的缓存机制,实现一套业务定制的缓存框架。

安全性

我们常听到GET不如POST安全,因为POST用body传输数据,而GET用url传输,更加容易看到。但是从攻击的角度,无论是GET还是POST都不够安全,因为HTTP本身就是明文协议。每个HTTP请求和返回的每个byte都会在网络上传播,不管是url,header还是body。这完全不是一个“是否容易在浏览器地址栏上看到“的问题。

因此如果开发者需要设计一个安全的传输模式,必须做到端到端的加密,即客户端到服务端的加密,业界通行做法是采用HTTPS协议。由于GET请求的参数更倾向于放在URL本身上,更容易被分享给第三方,在log中也会被记录下来,因此相对而言POST比GET安全些(但是POST放在body中的数据也可以被记录下来,只是在一些通行的软件如ngnix下,log默认记录的是URL等信息,如果需要记录body信息,需要开发者自行添加)

因此,绝大多数场景下,用POST + body里写私密数据,是合理的选择。一个典型的例子就是“登录”,同时如果还需要进一步的安全,可对这些数据进行加密,如采用HTTPS。

编码

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

在URL中,RFC1738规定只能采用ASCII,如果需要其他的字符,则需要重新编码,如中文常被编码为unicode码,以便通过ASCII形式发送

再讨论下Body。HTTP Body相对好些,因为有个Content-Type来比较明确的定义。

这里Content-Type会同时定义请求body的格式(application/x-www-form-urlencoded)和字符编码(UTF-8)。

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

回到POST,浏览器直接发出的POST请求就是表单提交,而表单提交只有application/x-www-form-urlencoded针对简单的key-value场景;和multipart/form-data,针对只有文件提交,或者同时有文件和key-value的混合提交表单的场景。

如果是Ajax或者其他HTTP Client发出去的POST请求,其body格式就非常自由了,常用的有json,xml,文本,csv……甚至是你自己发明的格式。只要前后端能约定好即可。

点赞

发表评论

邮箱地址不会被公开。 必填项已用*标注