首页
Preview

使用Go发送类型安全的HTTP请求

对于Gophers来说,我们基本上是为客户编写请求代码。有时我们需要请求第三方提供的RESTful API。这时,我们会感到组装请求很困难,不是难以实现,而是容易出错。

例如,如果我们想要发送如下请求,看起来很简单,但实际编写起来仍然很繁琐。

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.
    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://go-zero.dev/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-zero中的httpc包来实现类型安全。

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了。

通过定义请求类型并使用Do发送请求,httpc实现非常易于理解。我们的代码支持pathformheaderjson,非常容易和类型安全地发送HTTP请求。

更多功能

除了上述易用性和类型安全性之外,httpc包还具有以下功能。

  • 使用context控制超时。你可以为请求传递ctx
  • 自动集成OpenTelemetry。服务器返回的trace-idspan-id将自动写入日志,以便客户端和服务器共同解决问题。
  • 使用httpc.Service获取断路器功能。当服务器端出现问题时,它将自动停止发送请求,以避免无用的请求并减轻服务器端的压力。``` Want to Connect?You're welcome to use go-zero and star to support us!

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
菜鸟一只
你就是个黄焖鸡,又黄又闷又垃圾。

评论(0)

添加评论