3 minutes
Golearnings: HTTP requests
Golearnings will (hopefully) be a series of posts with stuff that I’ve been learning during my Go journey. This is not a tutorial. :)
One of things I love about Go is the fact that it is equiped with a lot of simple but solid tools, divided into many standard packages. When it comes to HTTP requests we have net/http
, which provides HTTP client and server implementations.
A basic request
This serves us fine if we want to make a quick GET/POST. However, we have no control over the request since http.Get
uses http
’s default client (basically the same as calling client.Do
on client := &http.Client{}
). Usually we’d want, at least, to define a timeout for the requests we’re making since the default has no timeout, which can potentially block your server.
Note: Take advantage of the constants exported by the
net/http
package for HTTP methods and status codes, such ashttp.MethodGet
andhttp.StatusOK
.
Headers
Having access to the request object is also handy to set request headers.
Close the Response Body
Don’t forget to close response.Body
. Not doing so results in a resource leak because the connection remains open and the file descriptor will not be freed.
Get that JSON into a struct
Let’s get the result of https://httpbin.org/json into a struct in our program. While we’re at it, let’s make the function a bit more real instead of simply logging the results.
This is the response JSON:
{
"slideshow": {
"author": "Yours Truly",
"date": "date of publication",
"slides": [
{
"title": "Wake up to WonderWidgets!",
"type": "all"
},
{
"items": [
"Why <em>WonderWidgets</em> are great",
"Who <em>buys</em> WonderWidgets"
],
"title": "Overview",
"type": "all"
}
],
"title": "Sample Slide Show"
}
}
Here’s what changed:
- the fuction now returns a pair of values
(*BinSlideshow, error)
- BinSlideshow is the struct type that will be used to represent the data (some of the response fields are left out on purpose)
- we create a new
json.Decoder
that will read fromres.Body
- finally,
json.Decode
will decode the byte stream into a BinSlideshow value.
Download a file
Let’s save the JSON response to a file instead.
This time we’ll create a file with os.Create
and use io.Copy
to copy the bytes into it.
Testing & mocking
With the approach used in the functions above there is no way to make unit tests without hitting the httpbin endpoint. That sucks. To solve that one can either pass the client as a dependency somehow (as an argument or as part of a struct).
Now, in my test I’ll create a mock client that implements Do
however I want. What I did:
- created a
mockClient
struct that holds a functionStubDo
with the stub implementation of the client’sDo
- declared a
Do
method ofmockClient
that executes the function stored inmockClient.StubDo
- for each test case I can change the behavior of
Do
as desired: notice response is different in the two test cases
This also allows to have default behavior for the implementations and some sort of stub reset, if needed (using defer
, for example).
I’m not entirely sure this is “the best way” but it made sense for me at some point.
There you go… A bunch of cool things using only Go’s standard packages and language features.
543 Words
2020-04-12