• 类型安全的 Go HTTP 请求


    前言

    对 Gopher 来说,虽然我们基本都是在写代码让别人来请求,但是有时候,我们也需要去请求第三方提供的 RESTful 接口,这个时候,我们才能感受到前端同学拼接 HTTP 请求参数的痛苦。

    比如,我们要发起类似这样一个请求,看起来很简单,实际写起来还是比较繁琐的。

    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 其实是蛮简单的,我们直接上手就可以写出来。

    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,抓包看一下,请求如下。各位不要往下看,你能想到失败的原因吗?

    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-zerohttpc 包是怎么使用并保证类型安全的。

    httpc 实现

    我们看看用 httpc 包来请求的代码怎么写。

    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)
    }
    

    写完测试一下,结果正如预期:

    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 就可以做到类型安全,并且代码非常精简。支持了如我们代码所示的 pathformheaderjson,可以非常方便且类型安全的发送 HTTP 请求。

    更多能力

    除了上面展示的简单易用和类型安全以外,httpc 包还有以下特点:

    1. context 的超时控制
    2. OpenTelemetry 自动集成,服务端返回的 trace-id, span-id 都会自动被记录到日志里,便于后续客户端、服务端协同查问题
    3. 可以通过 httpc.Service 来获得熔断能力,当服务端有问题,会自动熔断隔离请求,避免浪费时间等待和加剧服务端压力

    项目地址

    https://github.com/zeromicro/go-zero

    欢迎使用 go-zerostar 支持我们!

    微信交流群

    关注『微服务实践』公众号并点击 交流群 获取社区群二维码。

    如果你有 go-zero 的使用心得文章,或者源码学习笔记,欢迎通过公众号联系投稿!

  • 相关阅读:
    基于SSM 实现增删查改
    Unreal Engine游戏引擎的优势
    SpringBoot实现i18n国际化配置(超详细之跟着走就会系列)
    Jenkins 添加节点Node报错JNI error has occurred UnsupportedClassVersionError
    誉天在线项目~ElementPlus图片回显
    Hudi extraMetadata 研究总结
    opencv入门四
    Kafka MQ 生产者和消费者
    【Linux】UDP协议
    【深度学习】(问题记录)<对一个变量求梯度得到什么>-线性回归-小批量随机梯度下降
  • 原文地址:https://www.cnblogs.com/kevinwan/p/16300011.html