Introduction
Send a POST request in golang is pretty daunting if you have a post body and you’re coming from a scripting language like JavaScript or Python. Here in Go, schema for JSON needs to be defined beforehand in order to marshal and unmarshal string back and forth to JSON.
Simple POST
This marshaling/unmarshalling could be an oneliner if your request body is not nested, or is one level deep, or there is not even a body. Consider this example:
1
2
3
4
5
| func main() {
requestBody, err := json.Marshal(map[string]string{"key": "value"})
res, _ := http.Post("https://httpbin.org/post", "application/json", bytes.NewBuffer(requestBody))
}
|
POST with a Body
But imagine a request body like this, which is a sample body I am using to send a POST request to Elasticsearch:
1
2
3
4
5
6
7
8
9
10
| {
"sort": [ {"@timestamp": {"order": "desc"} }],
"query": {
"match": {
"path.to.keyword": {
"query": "the-term"
}
}
}
}
|
If it was JavaScript, I could have simply sent post request like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| let endpoint = "https://httpbin.org/post"
let query = {
"sort": [ {"@timestamp": {"order": "desc"} }],
"query": {
"match": {
"path.to.keyword": {
"query": "the-term"
}
}
}
}
fetch(endpoint, {
method: "POST",
body: JSON.stringify(query),
headers: {
"Content-Type": "application/json",
},
})
|
But this is not dealing with scripting language!
Create Schema, aka struct
To do this in go way, we need to do this in this manner:
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
| type Request struct {
Query Query `json:"query"`
Sort []Sort `json:"sort"`
}
type Sort struct {
Timestamp Timestamp `json:"@timestamp"`
}
type Timestamp struct {
Order string `json:"order"`
}
type Query struct {
Match Match `json:"match"`
}
type Match struct {
PathToKeyword PathToKeyword `json:"path.to.keyword"`
}
type PathToKeyword struct {
Query string `json:"query"`
}
|
The Driver Code
Followed by the driver code to make a POST request, which would be:
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
29
30
31
32
33
34
| package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
requestBody, err := json.Marshal(Response{
Query: Query{
Match: Match{
PathToKeyword: PathToKeyword{
Query: "the-term",
},
},
},
Sort: []Sort{
{
Timestamp: Timestamp{
Order: "desc",
},
},
},
})
res, _ := http.Post("https://httpbin.org/post", "application/json", bytes.NewBuffer(requestBody))
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(string(body))
}
|
Just for the sake of simplicity, I’ve decided not to handle errors.
Conclusion
You may find this nuance of go not very user friendly, but this was not one of the design golang of the language. And someone has well said… (in Gopher Slack)
Go is very much a non-magical language, you have to actually program :)
Related Links: