核心请求能力实现
约 1407 字大约 5 分钟
2025-05-07
请求需要考虑的问题
在进行核心能力实现之前, 我们需要思考下, 我们再实现的httpclient中, 都要支持什么能力? 这决定了后面的第三方库的选择, 或者自己实现的能力.
- 基础的请求发送, 包括GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH, CONNECT 和 TRACE, 这是一个基础能力.
- 为了对后端服务进行保护, 我们需要做流控验证.
- 第三方服务接口如果已知参数规则, 我们需要对参数进行校验.
- 简化外部调用时对参数的处理, 比如请求头, 请求参数, 请求体等, 以map输入, 自适应 xml/json 等请求方式.
- 简化外部调用时对响应结果的处理, 比如响应头, 响应体等, 以map输出, 自适应 xml/json 等响应方式.
- 对响应结果做数据缓存, 以减少对后端服务的请求次数.
以上就是我们在实现核心请求能力时, 需要考虑的问题, 上述问题本质即为三大类:
- 基础能力, 发送请求
- 请求前校验
- 请求后格式化与缓存
http库选择
golang内置的 http 请求库, 对于简单请求适用, 但是很多相对通用的逻辑, 依旧需要自己实现, 这里我们选择第三方库来进行实现.
第三方库选择 go-resty/resty/v3 , 注意我们使用的是 v3 最新版本, 文档地址: resty-v3文档.
resty 是一个 Go 语言的 HTTP 客户端库, 它提供了一种简单的方式来发送 HTTP 请求, 并处理响应.
resty 具有以下特点(更多能力自行查阅文档):
- 支持多种 HTTP 方法, 包括 GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH, CONNECT 和 TRACE.
- 支持非标请求, 如 GET请求存在Body
- 支持自动解压缩gzip结果
- 支持重定向自动跟随请求
- 支持设置请求头, 请求体, 请求参数, 请求超时时间, 重试次数等.
- 支持设置响应体的解析方式, 包括 JSON, XML, 文本, 二进制等.
- 支持设置请求的回调函数, 包括请求前, 响应后等.
- 支持设置请求的代理服务器.
- 支持设置请求的 Cookie.
- 支持设置请求的 TLS 配置.
- 支持设置请求的重试策略.
- 支持设置请求的日志记录策略.
基础请求发送无需多说, 各个第三方库必然支持, 请求前的校验与请求后的格式化与缓存, 我们可以通过 resty 的回调函数来实现. 即 RequestMiddleware
和 ResponseMiddleware
实现
具体实现方式不做赘述, 具体代码查看: resty请求实现
重点说几个需要注意的细节:
请求header
再http请求中, header key 的规范是首字母大写, 中划线分隔, 但是外部传入数据无法保证均符合这个规范, 所以我们需要对header key进行转换. 使用的是 textproto.CanonicalMIMEHeaderKey
进行转换, 传入key x-appid 会被转化为 X-Appid.
静态参数
对于一个请求, 我们有时候会需要设置一些固定参数, 如在header中设置请求来源信息等, 这些数据是固定值是不会发生变化的, 我们可以讲这些参数配置在 Static
中, static 为 map[string]map[string]any类型
, 定义 参数位置 => 指定位置的参数表, 如果指定为止已经存在同名参数, 则会使用static中的参数覆盖. static中的参数优先级最高
请求类型
前面说到, 为了简化使用, 传入的参数都是以 map
形式传入, 但是对于存在Body的请求, 我们需要 将参数根据请求的content_type, 实际转换成对应的格式
, 这一步的本质逻辑为: 基于不同的请求类型, 对map做不通方式的序列化, 然后写入请求body
, 通过接口约束, 来实现这一机制
接口约束
接口约束中, 只要实现一个write方法, 将body数据写入请求体即可.
// IRequestBodyWrite 请求信息写入Body的接口约束
type IRequestBodyWrite interface {
// Write 写入请求Body
Write(request *resty.Request, bodyData map[string]any) error
}
JSON写入
json写入是最为常见的一种请求方式, 没有特殊注意实现, 直接将map设置为Body即可, 具体实现参见: json请求body写入实现
form写入
application/x-www-form-urlencoded
请求方式, 需要将 map[string]any 转换为 map[string]string 进行处理, 此处需要注意 value 转换的方式, 不能无脑使用 fmt.Sprintf , 需要靠谱复杂数据类型的使用, 具体实现参见: json请求body写入实现
XML写入
xml请求方式, 除了一些特定场景或者比较古老服务, 使用不多, 需要特别注意的是, 在写入Body之前, 需要 将map转换为xml格式, 然后写入
, 具体实现参见: xml请求body写入实现
写入能力扩展
- 内部默认仅支持 json/form/xml 三种请求方式, 如果需要支持其他请求方式, 可以实现
IRequestBodyWrite
接口, 然后通过SetWriteBodyInstance
注册到WriteBodyInstanceTable
中即可. - 也可通过
RequestOption
来设置请求体写入方式(实现IRequestBodyWrite即可), 指定后, 不会再去内部的WriteBodyInstanceTable
中查找写入方式.