Sending type-safe HTTP requests with Go
Kevin Wan
Posted on May 23, 2022
Scenarios
For Gophers, we basically write code for clients to request. Sometimes we need to request RESTful APIs provided by third parties. At this time, we feel that it's hard to assemble the requests, not difficult, but error-prone.
For example, if we want to send a request like this, it looks very simple, but it's still tedious to actually write it.
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 native way
This API is actually quite simple and we can write it directly from scratch.
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)
}
We ran a test and found that we didn't get 200 OK
, dumped the packet and the request looks as follows. Can you think of the reason for the failure?
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}
The specific reasons for the failure will be discussed below, so let's explain this code first. You can see that the map[string]interface{}
is used for the splicing parameters, for each field we can't check if the type matches. Only when we send it out and receive 200 OK
from the server, we can confirm that it is passed correctly. For example, the type
parameter is used here as int
type, we may write it as string
type by mistake. But it is still hard to find out that this parameter is written incorrectly without requesting it.
So let's see how the httpc
package in go-zero
is used for type safety.
httpc
implementation
Let's see how the code for requesting with the httpc
package is written.
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)
}
Let's verify the code by sending a request, the result is as expected.
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}
Did you find out that, in contrast to the previous one, there is one more header was set, Content-Type: application/json; charset=utf-8
, and we forgot to set Content-Type
in the previous code.
httpc
implementation is very easy to understand by defining the type of the request and sending with Do
. With support for path
, form
, header
and json
as shown in our code, it is very easy and type-safe to send HTTP
requests.
More Capabilities
In addition to the ease of use and type-safety shown above, the httpc
package has the following features.
- timeout control with
context
. You can passctx
for the requests. - automatic integration of
OpenTelemetry
. Thetrace-id
,span-id
returned by the server will be automatically written in the log, so that the client and server can work together to investigate the problems. - use
httpc.Service
to get the circuit breaker ability. When the server side has problems, it will automatically stop sending the requests, to avoid useless requests and reduce the pressure on the server side.
Project address
https://github.com/zeromicro/go-zero
Welcome to use go-zero
and star to support us!
Posted on May 23, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.