学习链接
小林coding:GET 和 POST(看了一圈博客,还是小林哥写得好👍)
听说『99% 的人都理解错了 HTTP 中 GET 与 POST 的区别』??
GET和POST:辩证看100 continue,以及最根本区别
GET和POST的区别
GET | POST | |
---|---|---|
语义 | 从服务器获取指定的资源 | 根据请求负荷(报文body)对指定的资源做出处理 |
参数位置 | URL 中(query) | body 中 |
参数类型 | URL 只支持 ASCII,故需为 ASCII | 任意格式,通过 Content-Type 协商 |
参数长度 | 协议未做限制,浏览器对 URL 长度有限制 | 无限制 |
安全幂等 | 安全且幂等 | 不安全且不幂等 |
缓存书签 | 可缓存、可收藏为书签 | 不可缓存(大部分的实现),不可收藏为书签 |
只是 RFC 的规定,具体实现未必一定遵守,不遵守的后果根据不同的 Server 会有不同的表现。
要保证传输的安全应该通过 HTTPS 加密传输来保证,而不是在这两个方法中比较。
GET 的语义是从服务器获取指定的资源。
GET 请求的参数位置一般是写在 URL 中,URL 规定只能支持 ASCII,所以 GET 请求的参数只允许 ASCII 字符 ,而且浏览器会对 URL 的长度有限制(HTTP协议本身对 URL长度并没有做任何规定)。
POST 的语义是根据请求负荷(报文body)对指定的资源做出处理。
POST 请求携带数据的位置一般是写在报文 body 中, body 中的数据可以是任意格式的数据,只要客户端与服务端协商好即可,而且浏览器不会对 body 大小做限制。
安全和幂等
在 HTTP 协议里
- 「安全」是指请求方法不会「破坏」服务器上的资源。
- 「幂等」是指多次执行相同的操作,结果都是「相同」的。
从 RFC 规范定义的语义来看:
- GET 方法就是安全且幂等的,因为它是「只读」操作,无论操作多少次,服务器上的数据都是安全的,且每次的结果都是相同的。所以,可以对 GET 请求的数据做缓存,这个缓存可以做到浏览器本身上(彻底避免浏览器发请求),也可以做到代理上(如nginx),而且在浏览器中 GET 请求可以保存为书签。
- POST 因为是「新增或提交数据」的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以不是幂等的。所以,浏览器一般不会缓存 POST 请求,也不能把 POST 请求保存为书签。
GET 的语义是请求获取指定的资源。GET 方法是安全、幂等、可被缓存的。
POST 的语义是根据请求负荷(报文主体)对指定的资源做出处理,具体的处理方式视资源类型而不同。POST 不安全,不幂等,(大部分实现)不可缓存。
注意, 上面是从 RFC 规范定义的语义来分析的。
但是实际过程中,开发者不一定会按照 RFC 规范定义的语义来实现 GET 和 POST 方法。比如:
- 可以用 GET 方法实现新增或删除数据的请求,这样实现的 GET 方法自然就不是安全和幂等。
- 可以用 POST 方法实现查询数据的请求,这样实现的 POST 方法自然就是安全和幂等。
传输安全
如果「安全」放入概念是指信息是否会被泄漏的话,虽然 POST 用 body 传输数据,而 GET 用 URL 传输,这样数据会在浏览器地址拦容易看到,但是并不能说 GET 不如 POST 安全的。
因为 HTTP 传输的内容都是明文的,虽然在浏览器地址拦看不到 POST 提交的 body 数据,但是只要抓个包就都能看到了。
所以,要避免传输过程中数据被窃取,就要使用 HTTPS 协议,这样所有 HTTP 的数据都会被加密传输。
GET 请求可以带 body 吗?
RFC 规范并没有规定 GET 请求不能带 body 的。理论上,任何请求都可以带 body 的。只是因为 RFC 规范定义的 GET 请求是获取资源,所以根据这个语义不需要用到 body。
Client 在使用 GET 的时候可以加上 body, 但要不要接受或者使用是 Server 决定的。不同的 Server 不同的实现。
另外,URL 中的查询参数也不是 GET 所独有的,POST 请求的 URL 中也可以有参数的。
补充
关于 100-continue
发这个值只是“意思意思”, 不管响不响应, 客户端都可以继续发送 body,
除非服务端响应 417,客户端需要重新发一次不带这个值的请求报文。
不过发都发了的时候, 期待的其实是服务端的 100(Continue)。
客户端:
如果发了 100-continue, 收到了 100(Continue), 丢弃这个响应, 继续发 body;
如果没发 100-continue, 收到了 100(Continue), 忽略这个响应。
服务端:
如果收到了 100-continue, 并且在响应 100(Continue)之前,
就收到了部分或者全部的 body(或者表明没有body部分), 则可以不响应 100(Continue);
如果没收到 100-continue, 则不会响应 100(Continue)。
HTTP/1.1 才开始支持这个值
HTTP **100 Continue**
信息型状态响应码表示目前为止一切正常,客户端应该继续请求,如果已完成请求则忽略。
为了让服务器检查请求的首部,客户端必须在发送请求实体前,在初始化请求中发送 Expect: 100-continue
首部并接收 100 Continue
响应状态码。
其他补充
服务器在 Expect
字段接收到除了 100-continue
时,可能会返回 417(Expectation Failed)表示无法满足意外的期望值。
100 (Continue) 状态码表示请求报文的初始部分已经被收到了,并且还没有被服务端拒收。服务端打算在完整接收了整个请求并进行处理后,再发送最终的响应。
当收到的请求头中包含填写了 100-continue
的 Expect
字段时, 100 (continue)
响应表示服务器希望继续接收请求的 content
部分。客户端应该继续发送请求报文,并丢弃 100 (continue)
响应。
如果请求头中没有包含填写了 100-continue
的 Expect
字段,客户端可以直接忽略该临时响应。
客户端使用 Expect
的 100-continue
来通知接收者,客户端将在此请求中发送(可能很大)内容,并希望收到 100 (continue)
的临时响应。这样的情况一般出现在如果只凭借方法、目标 URI 和头部字段无法判断本次请求是否成功的时候。
这使得客户端能够在实际发送内容之前,获取到服务端的指示,以此来决定是否真正发送内容,
这样做可以在数据量很大或客户端预计可能出错的时候,提高效率
客户端策略:
- 当客户端要发送的请求报文中不包含
content
时,不应该使用100-continue
- 如果客户端希望收到
100 (continue)
响应,则必须先发送一个使用了100-continue
的报文 - 客户端在发送了一个用了
100-continue
的报文之后,没有被要求等待任何特定的时间长度。
事实上,即使还没收到响应,客户端也可以继续发送内容。
此外,由于使用 HTTP/1.0 的服务器不支持发送100 (Continue)
的响应,此时的客户端不应该在发送内容之前无期限地等待。 - 客户端发送了一个使用了
100-continue
的报文时,如果收到的响应是417 (Expection Failed)
,那么客户端应该重发一个不使用100-continue
的报文。因为417
仅仅只表明响应链上不支持Expect
。
服务端策略:
- 在 HTTP/1.0 请求中接收到
100-continue
的服务器必须忽略该值。 - 如果服务器已经接收到相应请求的部分或全部内容,或者如果帧指示没有内容,则服务器可以省略发送
100 (Continue)
响应。 - 发送
100 (Continue)
响应的服务器,一旦接收了并处理请求内容,则最后必须发送最终状态码,除非连接过早关闭。也就是说,100 (Continue)
不能用作最终响应。 - 在读取整个请求内容之前就以最终状态代码响应的服务器,应该表明它是否打算关闭连接或继续读取请求内容。
接收一个使用了 100-continue
的 HTTP/1.1 (或更新的协议) 请求 (带有方法、目标 URI、完整头部)时,源服务器必须在下面的两个响应中选择一个发送:
- 一个带有最终状态码的响应(如果仅通过请求报文的方法、目标 URI 和 头部字段就可判断状态)
- 一个
100 (Continue)
响应(表示希望客户端继续发送content
)
源服务器在发送 100 (Continue)
响应之前不能等待 content
。