HTTP Post request #
Make an HTTP Post request with a json request body.
package main import ( "bytes" "encoding/json" "io" "log" "net/http" "net/url" ) const BASE_URL = "jsonplaceholder.typicode.com" type PostBody struct { Title string `json:"title"` } func main() { // Prepare URL postURL := url.URL{ Host: BASE_URL, Path: "/todos", Scheme: "https", } // Prepare request body body := PostBody{ Title: "Hello World", } bodyBytes, err := json.Marshal(&body) if err != nil { log.Fatal(err) } reader := bytes.NewReader(bodyBytes) // Make HTTP POST request resp, err := http.Post(postURL.String(), "application/json", reader) if err != nil { log.Fatal(err) } // Close response body defer func() { err := resp.Body.Close() if err != nil { log.Fatal(err) } }() // Read response body responseBody, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } if resp.StatusCode >= 400 && resp.StatusCode <= 500 { log.Println("Error response. Status Code: ", resp.StatusCode) } log.Println("Response:", string(responseBody)) }
main.go
Custom HTTP Client #
The above uses the deafult http client š http.Post
. http.Post
calls the http.DefaultClient
, here is the behind the scenes code.
func Post(url, contentType string, body io.Reader) (resp *Response, err error) {
return DefaultClient.Post(url, contentType, body)
}
You can create custom http client š httpClient := http.Client{}
.
// Make HTTP POST request
httpClient := http.Client{}
resp, err := httpClient.Post(postURL.String(), "application/json", reader)
if err != nil {
log.Fatal(err)
}
Using http.Do #
Here is another way to make the request using the Do
function.
request, err := http.NewRequest(http.MethodPost, postURL.String(), reader)
if err != nil {
log.Fatal(err)
}
request.Header.Set("Content-Type", "application/json")
httpClient := &http.Client{}
resp, err := httpClient.Do(request)
if err != nil {
log.Fatal(err)
}
Reusable function #
As you can notice making a post request can be verbose, it involves a bunch of projects. What I do in some of my projects is create a reusable function to do some of the repeated stuff.
It would look something like this,
func JSONPost(url url.URL, body any) (string, error) {
bodyBytes, err := json.Marshal(&body)
if err != nil {
return "", nil
}
reader := bytes.NewReader(bodyBytes)
// Make HTTP POST request
request, err := http.NewRequest(http.MethodPost, url.String(), reader)
if err != nil {
return "", nil
}
request.Header.Set("Content-Type", "application/json")
httpClient := &http.Client{}
resp, err := httpClient.Do(request)
if err != nil {
return "", nil
}
// Close response body
defer func() {
err := resp.Body.Close()
if err != nil {
log.Fatal(err)
}
}()
// Read response body
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", nil
}
if resp.StatusCode >= 400 && resp.StatusCode <= 500 {
return string(responseBody), errors.New("400/500 status code error")
}
return string(responseBody), nil
}
And then use it,
func main() {
// Prepare URL
postURL := url.URL{
Host: BASE_URL,
Path: "/todos",
Scheme: "https",
}
// Prepare request body
body := PostBody{
Title: "Hello World",
}
responseBody, err := JSONPost(postURL, &body)
if err != nil {
log.Fatal(err)
}
log.Println("Response:", string(responseBody))
}
The signaure of reusable JSONPost
can be changed according to need, for example I usually also parse the response body into a struct as well instead of returning a string response.