本文作者:toString[1]

一、http 请求简介

理解 HTTP 构建的网络应用只要关注两个端--客户端(client)和服务端(server),两个端的交互来自 client 的 request,以及 server 端的 response。所谓的 http 服务器,主要在于如何接受 client 的 request,并向 client 返回 response。

接收 request 的过程中,最重要的莫过于路由(router),即实现一个 Multiplexer 器。Go 中既可以使用内置的 mutilplexer--DefaultServeMux,也可以自定义。Multiplexer 路由的目的就是为了找到处理器函数(hander),后者将对 request 进行处理,同时构建 response。

简单总结就是这个流程:

众所周知,在 golang 中实现的 http client 是自带连接池的。当我们做 http 请求时,极有可能就是复用了之前建立的 tcp 连接。那这个连接池是如何实现的,本文将带大家进行探究。

二、初始化客户端

对于 http 客户端,可以使用不同的实现了 RoundTripper 接口的 Transport 实现来配置它的行为,RoundTripper 有点像 http.Client 的中间件

三、golang 发起 GET 请求

3.1 GET 请求方法

3.2 基本的 GET 请求

请求示范:不带参数的 get 请求

3.3 带参数的 GET 请求

请求示范:带参数的 get 请求

四、golang 发起 POST 请求

4.1 POST 请求方法

4.2 不带参数的 POST 请求

请求示范:不带参数的 post 请求

4.3 不带参数的 POST 请求

请求示范:不带参数的 post 请求

五、小结

5.1 HTTP 协议请求方法

HTTP 协议定义了多种请求方式,具体如下:

  • GET:获取资源,用来请求访问已被 URI(统一资源标志符,和 URL 是包含和被包含的关系)识别的资源。
  • POST:用来传输实体的主体,虽然 GET 也可以实现,但是一般不用。
  • PUT:传输文件。但是鉴于 PUT 方法自身不带验证机制,任何人都可以上传文件,存在安全性问题,因此一般网站都不采用该方法。
  • HEAD:获得报文首部。和 GET 请求一样,只是不返回报文主体部分。
  • DELETE:删除文件。同样不带验证机制,存在安全性问题。
  • OPTIONS:询问指定的请求 URI 支持哪些方法。
  • TRACE:追踪路径,让 Web 服务器将之前的请求通信环回给客户端的方法。
  • CONNECT:要求在与代理服务器通信时建立隧道,实现隧道协议进行 TCP 通信。

5.2 POST 和 GET 请求的常见问题

  • 请求参数长度限制:GET 请求长度最多 1024kb,POST 对请求数据没有限制

关于此点,在 HTTP 协议中没有对 URL 长度进行限制,这个限制是不同的浏览器及服务器由于有不同的规范而带来的限制。

  • GET 请求一定不能用 request body 传输数据

GET 可以带 request body,但不能保证一定能被接收到。如果你用 GET 服务,在 request body 偷偷藏了数据,不同服务器的处理方式也是不同的,有些服务器会帮你读出数据,有些服务器直接忽略。

  • POST 比 GET 安全性要高

这里的安全是相对性,通过 GET 提交的数据都将显示到 URL 上,页面会被浏览器缓存,其他人查看历史记录会看到提交的数据,而 POST 不会。另外 GET 提交数据还可能会造成 CSRF 攻击。

  • GET 产生一个 TCP 数据包,POST 产生两个 TCP 数据包

对于 GET 方式的请求,浏览器会把 http header 和 data 一并发送出去,服务器响应 200 OK(返回数据); 而对于 POST,浏览器先发送 header,服务器响应 100 continue,浏览器再发送 data,服务器响应 200 OK(返回数据)。注意,尽管 POST 请求会分两次,但 body 是紧随在 header 后面发送的,根本不存在『等待服务器响应』一说。

5.3 POST 和 GET 请求的常见区别

  • 安全性:GET 在浏览器回退时是无害的,而 POST 会再次提交请求。
  • 安全性:GET 比 POST 更不安全,因为参数直接暴露在 URL 上,所以不能用来传递敏感信息。
  • 收藏为书签:GET 产生的 URL 地址可以被 Bookmark,而 POST 不可以。
  • 请求缓存:GET 请求会被浏览器主动 cache,而 POST 不会,除非手动设置。
  • 编码方式:GET 请求只能进行 url 编码,而 POST 支持多种编码方式。
  • 历史记录:GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留。
  • 长度限制:GET 请求在 URL 中传送的参数是有长度限制的,而 POST 没有。
  • 对参数的数据类型:GET 只接受 ASCII 字符,而 POST 没有限制。
  • 请求参数:GET 请求参数是通过 URL 传递的,多个参数以&连接,POST 请求放在 request body 中。

(本标准答案参考自 w3schools)

5.4 POST 和 GET 请求的本质区别

GET 和 POST 是什么?HTTP 协议中的两种发送请求的方法。

HTTP 是什么?HTTP 是基于 TCP/IP 的关于数据如何在万维网中如何通信的协议。

HTTP 的底层是 TCP/IP。所以 GET 和 POST 的底层也是 TCP/IP,也就是说,GET/POST 都是 TCP 链接。GET 和 POST 能做的事情是一样一样的。你要给 GET 加上 request body,给 POST 带上 url 参数,技术上是完全行的通的。

那为什么在以上的区别中,又说 GET 请求参数是通过 URL 传递的,POST 请求放在 request body 中呢?

在我大万维网世界中,TCP 就像汽车,我们用 TCP 来运输数据,它很可靠,从来不会发生丢件少件的现象。但是如果路上跑的全是看起来一模一样的汽车,那这个世界看起来是一团混乱,送急件的汽车可能被前面满载货物的汽车拦堵在路上,整个交通系统一定会瘫痪。为了避免这种情况发生,交通规则 HTTP 诞生了。HTTP 给汽车运输设定了好几个服务类别,有 GET, POST, PUT, DELETE 等等,HTTP 规定,当执行 GET 请求的时候,要给汽车贴上 GET 的标签(设置 method 为 GET),而且要求把传送的数据放在车顶上(url 中)以方便记录。如果是 POST 请求,就要在车上贴上 POST 的标签,并把货物放在车厢里。当然,你也可以在 GET 的时候往车厢内偷偷藏点货物,但是这是很不光彩;也可以在 POST 的时候在车顶上也放一些数据,让人觉得傻乎乎的。HTTP 只是个行为准则,而 TCP 才是 GET 和 POST 怎么实现的基本。

但是,我们只看到 HTTP 对 GET 和 POST 参数的传送渠道(url 还是 requrest body)提出了要求。“标准答案”里关于参数大小的限制又是从哪来的呢?

在我大万维网世界中,还有另一个重要的角色:运输公司。不同的浏览器(发起 http 请求)和服务器(接受 http 请求)就是不同的运输公司。虽然理论上,你可以在车顶上无限的堆货物(url 中无限加参数)。但是运输公司可不傻,装货和卸货也是有很大成本的,他们会限制单次运输量来控制风险,数据量太大对浏览器和服务器都是很大负担。业界不成文的规定是,(大多数)浏览器通常都会限制 url 长度在 2K 个字节,而(大多数)服务器最多处理 64K 大小的 url。超过的部分,恕不处理。如果你用 GET 服务,在 request body 偷偷藏了数据,不同服务器的处理方式也是不同的,有些服务器会帮你卸货,读出数据,有些服务器直接忽略,所以,虽然 GET 可以带 request body,也不能保证一定能被接收到哦。

好了,现在你知道,GET 和 POST 本质上就是 TCP 链接,并无差别。但是由于 HTTP 的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。

5.6 POST 和 GET 请求的 TCP 数据包发送区别

GET 和 POST 还有一个重大区别,简单的说:

  • GET 产生一个 TCP 数据包;POST 产生两个 TCP 数据包。

长的说:

  • 对于 GET 方式的请求,浏览器会把 http header 和 data 一并发送出去,服务器响应 200(返回数据);
  • 而对于 POST,浏览器先发送 header,服务器响应 100 continue,浏览器再发送 data,服务器响应 200 ok(返回数据)。
  • 也就是说,GET 只需要汽车跑一趟就把货送到了,而 POST 得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。

因为 POST 需要两步,时间上消耗的要多一点,看起来 GET 比 POST 更有效。因此 Yahoo 团队有推荐用 GET 替换 POST 来优化网站性能。但这是一个坑!跳入需谨慎。为什么?

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

5.7 以太坊节点 RPC 调用

以太坊节点的 RPC 请求只支持 POST,所以在调用以太坊系节点(例如 BSC、HECO)的 RPC 时,都使用 POS 即可

本系列文章:

从零开发区块链应用(一)--golang 配置文件管理工具 viper[2]

从零开发区块链应用(二)--mysql 安装及数据库表的安装创建[3]

从零开发区块链应用(三)--mysql 初始化及 gorm 框架使用[4]

从零开发区块链应用(四)--自定义业务错误信息[5]

从零开发区块链应用(五)--golang 网络请求[6]

参考资料

[1]

toString: https://learnblockchain.cn/people/3835

[2]

从零开发区块链应用(一)--golang配置文件管理工具viper: https://learnblockchain.cn/article/3446

[3]

从零开发区块链应用(二)--mysql安装及数据库表的安装创建: https://learnblockchain.cn/article/3447

[4]

从零开发区块链应用(三)--mysql初始化及gorm框架使用: https://learnblockchain.cn/article/3448

[5]

从零开发区块链应用(四)--自定义业务错误信息: https://learnblockchain.cn/article/3449

[6]

从零开发区块链应用(五)--golang网络请求: https://learnblockchain.cn/article/3457