HTTP最早被用来做浏览器与服务器之间交互html和表单的通讯协议;后来又被被广泛的扩充到接口格式的定义上。所以在讨论get和post区别的时候,需要现确定下到底是浏览器使用的get/post还是用http作为接口传输协议的场景。
浏览器的GET和POST
这里特指浏览器中非Ajax的http请求,即从html和浏览器诞生就一直使用的http协议中的get/post。浏览器用GET请求来获取一个html页面/图片/css/js等资源;用post来提交一个<form>表单,并得到一个结果的网页。
get
“读取“一个资源。比如get到一个html文件。反复读取不应该对访问的数据有副作用。比如”get一下,用户就下单了,返回订单已受理“,这是不可接受的。这里get没有副作用被称为“幂等“(Idempotent)。
get因为是读取,就可以对get请求的数据做缓存。这个缓存可以做到浏览器本身上(彻底避免浏览器发请求),也可以做到代理上(如nginx),或者做到server端(协商缓存,用Etag,至少可以减少带宽消耗)
post
在页面里<form> 标签会定义一个表单。点击其中的submit元素会发出一个POST请求让服务器做一件事。这件事往往是有副作用的,不是幂等的。
不幂等也就意味着不能随意多次执行。因此也就不能缓存。比如通过POST下一个单,服务器创建了新的订单,然后返回订单成功的界面。这个页面不能被缓存。试想一下,如果POST请求被浏览器缓存了,那么下单请求就可以不向服务器发请求,而直接返回本地缓存的“下单成功界面”,却又没有真的在服务器下单。那是一件多么滑稽的事情。
当然,服务器的开发者完全可以把GET实现为有副作用;把POST实现为没有副作用。只不过这和浏览器的预期不符。
GET和POST携带数据的格式也有区别。当浏览器发出一个GET请求时,就意味着要么是用户自己在浏览器的地址栏输入,要不就是点击了html里a标签的href中的url。所以其实并不是GET只能用url,而是浏览器直接发出的GET只能由一个url触发。所以没办法,GET上要在url之外带一些参数就只能依靠url上附带querystring。但是HTTP协议本身并没有这个限制。
浏览器的POST请求都来自表单提交。每次提交,表单的数据被浏览器用编码到HTTP请求的body里。
浏览器发出的POST请求的body主要有有两种格式,一种是application/x-www-form-urlencoded用来传输简单的数据,大概就是"key1=value1&key2=value2"这样的格式。另外一种是传文件,会采用multipart/form-data格式。
浏览器在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协议本身看,并没有什么限制说GET一定不能没有body,POST就一定不能把参放到<URL>的querystring上。因此其实可以更加自由的去利用格式,只要请求的客户端和服务器端能够约定好。
关于安全性
get请求的参数更倾向于放在url上,因此有更多机会被泄漏。比如携带私密信息的url会展示在地址栏上,还可以分享给第三方,就非常不安全了。此外,从客户端到服务器端,有大量的中间节点,包括网关,代理等。他们的access log通常会输出完整的url,比如nginx的默认access log就是如此。如果url上携带敏感数据,就会被记录下来。但就算私密数据在body里,也是可以被记录下来的,因此如果请求要经过不信任的公网,避免泄密的唯一手段就是https。
如果是用作接口,GET实际上也可以带body,POST也可以在url上携带数据。所以实际上到底怎么传输私密数据,要看具体场景具体分析。当然,绝大多数场景,用POST + body里写私密数据是合理的选择。
主要区别
GET产生一个TCP数据包,POST产生两个TCP数据包
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
原文:
https://www.zhihu.com/question/28586791