前言
对 Gopher 来说,虽然我们基本都是在写代码让别人来请求,但是有时候,我们也需要去请求第三方提供的 RESTful 接口,这个时候,我们才能感受到前端同学拼接 HTTP 请求参数的痛苦。
比如,我们要发起类似这样一个请求,看起来很简单,实际写起来还是比较繁琐的。
1
2
3
4
|
POST /articles/ 5 /update?device=ios HTTP/ 1.1 Host: go-zero.dev Authorization: Bearer <jwt-token> { "author" : "kevin" , "body" : "this is not important!" , "title" : "my title" , "type" : 6 } |
Go 原生写法
这个 API 其实是蛮简单的,我们直接上手就可以写出来。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
func main() { var buf bytes.Buffer encoder := json.NewEncoder(&buf) params := map [ string ] interface {}{ "title" : "my title" , "body" : "this is not important!" , "author" : "kevin" , "type" : 6 , } if err := encoder.Encode(params); err != nil { fmt.Fprintln(os.Stderr, err) return } url := fmt.Sprintf( "http://localhost:3333/articles/%d/update?device=%s" , 5 , "ios" ) req, err := http.NewRequest(http.MethodPost, url, &buf) if err != nil { fmt.Fprintln(os.Stderr, err) return } req.Header.Add( "Authorization" , "Bearer <jwt-token>" ) cli := http.Client{} resp, err := cli.Do(req) if err != nil { fmt.Fprintln(os.Stderr, err) return } io. Copy (os.Stdout, resp.Body) } |
我们跑了测试一下,发现没有得到 200 OK,抓包看一下,请求如下。各位不要往下看,你能想到失败的原因吗?
1
2
3
4
5
6
7
|
POST /articles/ 5 /update?device=ios HTTP/ 1.1 Host: go-zero.dev User-Agent: Go-http-client/ 1.1 Content-Length: 79 Authorization: Bearer <jwt-token> Accept-Encoding: gzip { "author" : "kevin" , "body" : "this is not important!" , "title" : "my title" , "type" : 6 } |
具体失败原因这里就不细讲了,我们先来分析这段代码。可以看到其中为了拼接参数使用了 map[string]interface{},对于其中每个字段我们是不能校验类型是否匹配的,只有发送出去了,收到了服务端的 200 OK,我们才能确认传对了。比如其中的 type 参数,这里是使用了 int 类型,我们可能顺手写成 string 类型,但是不请求我们还是很难发现这个参数写错了的。
那么让我们看看 go-zero 里 httpc 包是怎么使用并保证类型安全的。
httpc 实现
我们看看用 httpc 包来请求的代码怎么写。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
const url = "http://go-zero.dev/articles/:id/update" type UpdateArticle struct { ID int `path: "id" ` Device string `form: "device,options=ios,android,web,desktop" ` Authorization string `header: "Authorization" ` Title string `json: "title" ` Body string `json: "body" ` Author string `json: "author" ` Type int `json: "type" ` } func main() { data := &UpdateArticle{ ID: 5 , Device: "ios" , Authorization: "Bearer <jwt-token>" , Title: "my title" , Body: "this is not important!" , Author: "kevin" , Type : 6 , } resp, err := httpc.Do(context.Background(), http.MethodPost, url, data) if err != nil { fmt.Fprintln(os.Stderr, err) return } io. Copy (os.Stdout, resp.Body) } |
写完测试一下,结果正如预期:
1
2
3
4
5
6
7
8
|
POST /articles/ 5 /update?device=ios HTTP/ 1.1 Host: go -zero.dev User-Agent: Go -http-client/ 1.1 Content-Length: 79 Content- Type : application/json; charset=utf- 8 Authorization: Bearer <jwt-token> Accept-Encoding: gzip { "author" : "kevin" , "body" : "this is not important!" , "title" : "my title" , "type" : 6 } |
你发现了没有,跟前面的对比,其中多了 Content-Type: application/json; charset=utf-8,而我们之前写法里忘记设置 Content-Type 了。
而 httpc 的写法只要定义好请求的类型,然后通过 httpc.Do 就可以做到类型安全,并且代码非常精简。支持了如我们代码所示的 path、form、header 和 json,可以非常方便且类型安全的发送 HTTP 请求。
更多能力
除了上面展示的简单易用和类型安全以外,httpc 包还有以下特点:
- context 的超时控制
- OpenTelemetry 自动集成,服务端返回的 trace-id, span-id 都会自动被记录到日志里,便于后续客户端、服务端协同查问题
- 可以通过 httpc.Service 来获得熔断能力,当服务端有问题,会自动熔断隔离请求,避免浪费时间等待和加剧服务端压力
以上就是Go类型安全的HTTP请求示例详解的详细内容,更多关于Go类型安全HTTP请求的资料请关注服务器之家其它相关文章!
原文链接:https://juejin.cn/post/7100734121135538207