1.GET 和 POST 到底有什么区别? - 大宽宽的回答 - 知乎
提交数据的形式:
- GET:附在URL后,展现在地址栏中
- POST:数据放在请求字段中
提交数据的大小:
- GET:影响了URL的长度,限制URL长度的是客户端和服务器的支持的不同
- POST:起限制作用的是服务器处理程序的处理能力
安全性:
- GET:明文出现在URL上,比POST弱
- (1)登录页面有可能被浏览器缓存
- (2)其他人查看浏览器的历史纪录,那么别人就可 以拿到你的账号和密码了
- (3)当遇上跨站的攻击时,安全性的表现更差了
1.1 场景一:浏览器使用的GET/POST
浏览器中非AJAX的HTTP请求,HTTP协议中的GET/POST
浏览器用GET请求来获取一个html页面/图片/css/js等资源;用POST来提交一个<form>表单,并得到一个结果的网页
1.1.1 GET
“读取“一个资源,反复读取不应该对访问的数据有副作用 [没有副作用被称为“幂等“(Idempotent)]
可以对GET请求的数据做缓存:
- 缓存可以做到浏览器本身上(彻底避免浏览器发请求)
- 也可以做到代理上(如nginx)
- 或者做到server端(用Etag,至少可以减少带宽消耗)
1.1.2 POST
<form> 标签定义一个表单,点击其中的submit元素发出一个POST请求让服务器做一件事。
- 有副作用的
- 不幂等的——不能随意多次执行——不能缓存——不能把POST请求保存为书签——尝试重新执行POST请求,浏览器也会弹一个框提示下这个刷新可能会有副作用,询问要不要继续
泛泛的说“GET请求没有body,只有url,请求数据放在url的querystring中;POST请求的数据在body中“。但这种情况仅限于浏览器发请求的场景
1.2 场景二:接口中的GET和POST
指通过浏览器的Ajax api,或者iOS/Android的App的http client,java的commons-httpclient/okhttp或者是curl,postman之类的工具发出来的GET和POST请求。此时GET/POST不光能用在前端和后端的交互中,还能用在后端各个子服务的调用中(即当一种RPC协议使用)。
为什么使用这个:http本身已经有大量的现成的支持工具可以使用,并且对人类很友好,容易debug。HTTP协议在微服务中的使用是相当普遍的。
HTTP请求的格式:
<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等
参数可以放在url的path里,querystring里,body里,header里
因此,制定接口规范/风格,如REST:
REST充分运用GET、POST、PUT和DELETE,约定了这4个接口分别获取、创建、替换和删除“资源”,REST最佳实践还推荐在请求体使用json格式。这样仅仅通过看HTTP的method就可以明白接口是什么意思,并且解析格式也得到了统一。
REST中GET和POST不是随便用的。
在REST中, 【GET】 + 【资源定位符】被专用于获取资源或者资源列表。与浏览器的场景类似,REST GET也不应该有副作用,于是可以被反复无脑调用。浏览器(包括浏览器的Ajax请求)对于这种GET也可以实现缓存。
REST 【POST】+ 【资源定位符】则用于“创建一个资源”——浏览器中用来实现表单提交的POST,和REST里实现创建资源的POST语义上的不同。
POST http://foo.com/books
{
"title": "大宽宽的碎碎念",
"author": "大宽宽",
...
}
REST POST和REST PUT的区别(有些api是使用PUT作为创建资源的Method)
PUT与POST的区别在于,PUT的实际语义是“replace”replace。REST规范里提到PUT的请求体应该是完整的资源,包括id在内。服务器应该先根据请求提供的id进行查找,如果存在一个对应id的元素,就用请求中的数据整体替换已经存在的资源;如果没有,就用“把这个id对应的资源从【空】替换为【请求数据】“。直观看起来就是“创建”了。
与PUT相比,POST更像是一个“factory”,通过一组必要的数据创建出完整的资源。
1.3 安全性
无论是GET还是POST都不够安全,因为HTTP本身是明文协议。每个HTTP请求和返回的每个byte都会在网络上明文传播,不管是url,header还是body。
为了避免传输中数据被窃取,必须做从客户端到服务器的端端加密。业界的通行做法就是https——即用SSL协议协商出的密钥加密明文的http数据。这个加密的协议和HTTP协议本身相互独立。如果是利用HTTP开发公网的站点/App,要保证安全,https是最最基本的要求。
//端端加密并不一定非得用https。比如国内金融领域都会用私有网络,也有GB的加密协议SM系列。
GET请求的参数更倾向于放在url上,因此有更多机会被泄漏。
从客户端到服务器端,有大量的中间节点,包括网关,代理等。他们的access log通常会输出完整的url,比如nginx的默认access log就是如此。如果url上携带敏感数据,就会被记录下来。但请注意,就算私密数据在body里,也是可以被记录下来的,因此如果请求要经过不信任的公网,避免泄密的唯一手段就是https。这里说的“避免access log泄漏“仅仅是指避免可信区域中的http代理的默认行为带来的安全隐患。
一般情况下,私密数据传输用POST + body就好。
1.4 HTTP请求
“请求头”:所有的“控制类”信息应该放在请求头中
“请求体”:具体的数据放在请求体里
服务器端在解析时,总是会先完全解析全部的请求头部。这样,服务器端总是希望能够了解请求的控制信息后,就能决定这个请求怎么进一步处理,是拒绝,还是根据content-type去调用相应的解析器处理数据,或者直接用zero copy转发。
为了进一步优化,客户端可以利用HTTP的Continued协议来这样做:客户端总是先发送所有请求头给服务器,让服务器校验。如果通过了,服务器回复“100 - Continue”,客户端再把剩下的数据发给服务器。如果请求被拒了,服务器就回复个400之类的错误,这个交互就终止了。这样,就可以避免浪费带宽传请求体。但是代价就是会多一次Round Trip。如果刚好请求体的数据也不多,那么一次性全部发给服务器可能反而更好。
基于此,客户端就能做一些优化,比如内部设定一次POST的数据超过1KB就先只发“请求头”,否则就一次性全发。客户端甚至还可以做一些Adaptive的策略,统计发送成功率,如果成功率很高,就总是全部发等等。不同浏览器,不同的客户端(curl,postman)可以有各自的不同的方案。不管怎样做,优化目的总是在提高数据吞吐和降低带宽浪费上做一个折衷。
从HTTP协议的角度,“请求头”就是Method + URL(含querystring)+ Headers;再后边的都是请求体。
从业务角度,如果你把一次请求立即为一个调用的话。这一行函数名和两个参数都可以看作是一个请求,不区分头和体。
对于HTTP,需要区分【头】和【体】,Http Request和Http Response都这么区分。Http这么干主要用作:
- 对于HTTP代理
- 支持转发规则,比如nginx先要解析请求头,拿到URL和Header才能决定怎么做(转发proxy_pass,重定向redirect,rewrite后重新判断……)
- 需要用请求头的信息记录log。尽管请求体里的数据也可以记录,但一般只记录请求头的部分数据。
- 如果代理规则不涉及到请求体,那么请求体就可以不用从内核态的page cache复制一份到用户态了,可以直接zero copy转发。这对于上传文件的场景极为有效。
- ……
- 对于HTTP服务器
- 可以通过请求头进行ACL控制,比如看看Athorization头里的数据是否能让认证通过
- 可以做一些拦截,比如看到Content-Length里的数太大,或者Content-Type自己不支持,或者Accept要求的格式自己无法处理,就直接返回失败了。
- 如果body的数据很大,利用Stream API,可以方便支持一块一块的处理数据,而不是一次性全部读取出来再操作,以至于占用大量内存。
- ……
1.4.1 http请求报文结构
一个HTTP请求报文由四个部分组成:请求行、请求头部、空行、请求数据
- 请求行:由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。
- 方法字段就是HTTP使用的请求方法,比如常见的GET/POST、PUT、DELETE、OPTION
- HTTP协议版本有两种:HTTP1.0/HTTP1.1
- HTTP1.0对于每个连接都只能传送一个请求和响应,请求就会关闭,HTTP1.0没有Host字段;
- 而HTTP1.1在同一个连接中可以传送多个请求和响应,多个请求可以重叠和同时进行,HTTP1.1必须有Host字段。
- 请求头部:大多数请求头并不是必需的,但Content-Length除外。对于POST请求来说 Content-Length必须出现。
- 常见的请求头字段:Accept、Accept-Charset、Accept-Encoding、Accept-Language、Content-Length、Cookie等
- 空行:告诉服务器请求头部到此为止
- 请求数据:
-
若方法字段是GET,则此项为空,没有数据
-
若方法字段是POST,则通常来说此处放置的就是要提交的数据
-
1.4.2 HTTP响应报文
HTTP响应报文也由三部分组成:响应行、响应头、响应体
- 响应行:一般由协议版本、状态码及其描述组成
- 协议版本HTTP/1.1或者HTTP/1.0
- 状态码:
- 200:这个是最常见的http状态码,表示服务器已经成功接受请求,并将返回客户端所请求的最终结果
- 301:永久重定向
- 302:临时重定向
- 404:资源未找到
- 403:服务器拒绝访问
- 500:服务器遇到未知的错误
- 503:服务器由于临时的服务器过载或者是维护,