6 minutes read

In this topic, we are going to talk about HTTP (Hypertext Transfer Protocol) requests in Go. Developers make HTTP requests all the time, and Golang has a built-in package net/http. The HTTP package offers convenient functions like Get, Post, and Head for common HTTP requests. We're going to explore the first two of them below.

GET Requests

GET requests are used to receive data over the HTTP protocol. These requests don't modify the data but return the state of some resource or page. For example, we can use it to get information about users.

We will make use of the httpbin.org site to help us inspect the HTTP requests. Have a look at the following piece of code:

package main

import (
    "fmt"
    "io"
    "log"
    "net/http"
)

func main() {
    response, err := http.Get("https://httpbin.org/get")
    if err != nil {
        log.Fatal(err)
    }
    defer response.Body.Close() // don't forget to close body

    body, err := io.ReadAll(response.Body)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(string(body))
    fmt.Println(response.Status)
    fmt.Println(response.StatusCode)
    if response.StatusCode >= 200 && response.StatusCode < 300 {
        fmt.Println("request is successful")
    }
}

In this example, call http.Get, which gives us a response and the error value. If there occurred an error in making this request, the err variable would be not nil. We would log this error and quit.

The response variable contains the server reply, such as body, status, protocol version, etc. The Body may contain any information at the discretion of the server. For example, JSON, XML, string, or other types. The Status and StatusCode are used to inform about the success or failure of the request. If the request code is in the form of 2**, it means that the server has been able to fulfill our request fully, and we've made the request correctly.

The next step is deferring the execution of response.Body.Close(), which is done at the end of the function. You should pay attention to this step because when you have the response body (it is not nil), forgetting to close it can cause resource leaks in long-running programs.

In continuation, we read the entirety of the response body and log it. The response.Body implements the io.Reader interface, allowing us to use the io.ReadAll function. In our case, we do know that the target response is small in size. This gives us the confidence to read the full response in memory. However, since the response body is an io.Reader, you could read the data chunk by chunk and process it as a stream of data.

In our case, the body contains the JSON that was sent to us in response to our request. If we run this code, this is what we get as the output:

{
  "args": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Host": "httpbin.org", 
    "User-Agent": "Go-http-client/2.0", 
    "X-Amzn-Trace-Id": "Root=1-626eacd4-169ef1d3273fe4f723a374ac"
  }, 
  "origin": "146.120.71.159", 
  "url": "https://httpbin.org/get"
}

200 OK
200
request is successful

POST Requests

The HTTP POST method is usually used to create or add a resource to the server. For example, to create a new user. The POST request differs from the GET one in that we can not only receive but also send the Body in the POST request.

package main

import (
    "bytes"
    "fmt"
    "io"
    "log"
    "net/http"
)

func main() {
    response, err := http.Post("https://httpbin.org/post", "text/plain", bytes.NewBufferString("example"))
    if err != nil {
        log.Fatal(err)
    }
    defer response.Body.Close()

    body, err := io.ReadAll(response.Body)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(string(body))
}

In the above example, we're going to send a payload in the body of the request. We create and initialize a new buffer using a string, and then call http.Post. This function takes the URL, the content type we are currently using ("text/plain", in our case), and io.Reader. In our hands, we have a string that doesn't implement this interface, so we use bytes.NewBufferString. It gives us a bytes buffer based on our string. Just like the last time, we log the server response to see if our request has been successful:

{
  "args": {}, 
  "data": "example", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Content-Length": "7", 
    "Content-Type": "text/plain", 
    "Host": "httpbin.org", 
    "User-Agent": "Go-http-client/2.0", 
    "X-Amzn-Trace-Id": "Root=1-628c43b6-228e76fe3f028bf022656098"
  }, 
  "json": null, 
  "origin": "146.120.77.158", 
  "url": "https://httpbin.org/post"
}

Conclusion

Now that you know some basics about HTTP requests in Go, you can write simple GET and POST requests without any problem. Don't forget to close the request body!

9 learners liked this piece of theory. 1 didn't like it. What about you?
Report a typo