This chapter will equip you to use the Go HTTP client to talk to other systems over the internet.
You will start by learning to use the HTTP client to get data from a web server and to send data to a web server. By the end of the chapter, you will be able to upload a file to a web server and experiment with a custom Go HTTP client to interact with web servers.
In the previous chapter, you looked at SQL and databases. You learned how to execute queries, how to create tables, how to insert data into tables and fetch data, how to update data, and how to delete data within a table.
In this chapter, you will learn about the Go HTTP client and how to use it. An HTTP client is something that is used to get data from or send data to a web server. Probably the most well-known example of an HTTP client is a web browser (such as Firefox). When you enter a web address into a web browser, it will have an HTTP client built in that sends a request to the server for data. The server will gather the data and send it back to the HTTP client, which will then display the web page in the browser. Similarly, when you fill out a form in a web browser, for example, when you log in to a website, the browser will use its HTTP client to send that form data to the server and then take appropriate action depending on the response.
This chapter looks at how you can use the Go HTTP client to request data from a web server and send data to a server. You will examine the different ways you can use the HTTP client to interact with a web server and the various use cases for those interactions. The web browser example will be useful in explaining the different interactions. As part of this chapter, you will create your own Go programs that make use of the Go HTTP client to send and receive data from a web server.
The Go HTTP client is part of the Go standard library, specifically the net/http library. There are two main ways to use it. The first is to use the default HTTP client that is included in the net/http library. It's simple to use and allows you to get up and running quickly. The second way is to create your own HTTP client based on the default HTTP client. This allows you to customize the requests and various other things. It takes longer to configure, but it gives you much more freedom and control over the requests you send.
When using an HTTP client, you can send different types of requests. While there are many types of requests, we will discuss the two main ones, the GET request and the POST request. For instance, if you wanted to retrieve data from a server, you would send a GET request. When you enter a web address in your web browser, it will send a GET request to the server at that address and then display the data it returns. If you wanted to send data to the server, you would send a POST request. If you wanted to log into a website, you would POST your login details to the server.
In this chapter, there are a few exercises to teach you about the Go HTTP client. They will teach you how to request data from a server in various formats using GET requests. They will also teach you how to POST form data to a web server, similar to how a web browser would send a POST request when you log in to a website. These exercises will also show you how to upload a file to a web server and how to use a customized HTTP client to have more control over the requests you send.
When you want to retrieve data from a web server, you send a GET request to the server. When sending a request, the URL will contain the information on the resource you want data from. The URL can be broken down into a few key parts. These include the protocol, the hostname, the URI, and the query parameters. The format of it looks like this:
In this example:
In this exercise, you will be getting data from a web server and printing out that data. You will send a GET request to https://www.google.com and display the data the web server returns:
Note
For this topic, you will need to have Go installed and GOPATH set up on your system. You will also need an IDE that you can use to edit .go files.
package main
import (
"io/ioutil"
"log"
"net/http"
Now that you have the package set up and the imports you need, you can start creating a function to get data from a web server. The function you are going to create will request data from a web server.
func getDataAndReturnResponse() string {
r, err := http.Get("https://www.google.com")
if err != nil {
log.Fatal(err)
}
defer r.Body.Close()
data, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
}
return string(data)
}
The function you have now created will now look like this:
func getDataAndReturnResponse() string {
// send the GET request
r, err := http.Get("https://www.google.com")
if err != nil {
log.Fatal(err)
}
// get data from the response body
defer r.Body.Close()
data, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
}
// return the response data
return string(data)
}
func main() {
data := getDataAndReturnResponse()
log.Println(data)
}
go run server.go
The program will issue a GET request to https://www.google.com and log the response in your terminal.
While it may look like gibberish, if you were to save that data to a file called response.html and open it in your web browser, it would resemble the Google home page. This is what your web browser will do under the hood when you open a web page. It will send a GET request to the server and then display the data it returns. If we do this manually, it will look as follows:
In this exercise, we saw how to send a GET request to a web server and get data back. You created a Go program that sent a request to https://www.google.com and got back the HTML data for the Google home page.
Once you have requested data from a server, the data returned can come in various formats. For example, if you send a request to packtpub.com, it will return HTML data for the Packt website. While HTML data is useful for displaying websites, it isn't ideal for sending machine-readable data. A common data type used in web APIs is JSON. JSON provides a good structure for data that is both machine-readable and human-readable. Later, you will learn how to parse JSON and make use of it using Go.
In this exercise, you will parse structured JSON data in Go. The server will return JSON data and you will use the json.Unmarshal function to parse the data and put it into a struct:
package main
import (
"log"
"net/http"
)
type server struct{}
func (srv server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
msg := "{"message": "hello world"}"
w.Write([]byte(msg))
}
func main() {
log.Fatal(http.ListenAndServe(":8080", server{}))
}
This creates a very basic web server that sends back JSON data. We will explain in more detail how this works in the next chapter. For now, we will just use it as an example.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)
type messageData struct {
Message string `json:"message"`
}
func getDataAndReturnResponse() messageData {
When you run the web server, it will listen on http://localhost:8080. So, you need to send a GET request to that URL and then read the response body:
r, err := http.Get("http://localhost:8080")
if err != nil {
log.Fatal(err)
}
defer r.Body.Close()
data, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
}
message := messageData{}
err = json.Unmarshal(data, &message)
if err != nil {
log.Fatal(err)
}
This will populate the message variable with the data returned from the server.
return message
func main() {
data := getDataAndReturnResponse()
fmt.Println(data.Message)
}
go run server.go
In this exercise, you sent a GET request to the server and got back structured data in JSON format. You then parsed that JSON data to get the message from it.
Imagine you are interacting with a web API. You send a GET request for data and get back an array of names. You need to count those names to find out how many of each you have. In this activity, you will do just that. You will send a GET request to the server, get back structured JSON data, parse the data, and count how many of each name you got back in the response:
The expected output is as follows:
Note
The solution for this activity can be found on page 752.
In this activity, we have requested data from a web server and processed the data it returned using the Go HTTP client.
In addition to requesting data from a server, you will also want to send data to a server. The most common way of doing this is via a POST request. A POST request comes in two main parts: the URL and the body. The body of a POST request is where you put the data you want to send to the server. A common example of this is a login form. When we send a login request, we POST the body to the URL. The web server then checks that the login details within the body are correct and updates our login status. It responds to the request by telling the client whether it succeeded or not. In this chapter, you will learn how to send data to a server using a POST request.
In this exercise, you will send a POST request to a web server containing a message. The web server will then respond with the same message so you can confirm that it received it:
package main
import (
"encoding/json"
"log"
"net/http"
)
type server struct{}
type messageData struct {
Message string `json:"message"`
}
func (srv server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
jsonDecoder := json.NewDecoder(r.Body)
messageData := messageData{}
err := jsonDecoder.Decode(&messageData)
if err != nil {
log.Fatal(err)
}
jsonBytes, _ := json.Marshal(messageData)
log.Println(string(jsonBytes))
w.Write(jsonBytes)
}
func main() {
log.Fatal(http.ListenAndServe(":8080", server{}))
}
This creates a very basic web server that receives a JSON POST request and returns the message sent to it back to the client.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)
type messageData struct {
Message string `json:"message"`
}
func postDataAndReturnResponse(msg messageData) messageData {
jsonBytes, _ := json.Marshal(msg)
r, err := http.Post("http://localhost:8080", "application/json", bytes.NewBuffer(jsonBytes))
if err != nil {
log.Fatal(err)
}
defer r.Body.Close()
data, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
}
message := messageData{}
err = json.Unmarshal(data, &message)
if err != nil {
log.Fatal(err)
}
return message
func main() {
msg := messageData{Message: "Hi Server!"}
data := postDataAndReturnResponse(msg)
fmt.Println(data.Message)
}
In this exercise, you sent a POST request to the server. The server parsed the request and sent the same message back to you. If you change the message sent to the server, you should see the response from the server sending back the new message.
Another common example of data you might want to POST to a web server is a file from your local computer. This is how websites allow users to upload their photos and so on. As you can imagine, this is a little more complex than sending simple form data. To achieve this, the file needs to be read first, then wrapped in a format that the server can understand. It can then be sent in a POST request to the server in what's called a multipart form. You will learn how to read in a file and upload it to a server using Go.
In this exercise, you will read in a local file and then upload it to a web server. You can then check that the web server saved the file you uploaded:
server.go
9 func (srv server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
10 uploadedFile, uploadedFileHeader, err := r.FormFile("myFile")
11 if err != nil {
12 log.Fatal(err)
13 }
14 defer uploadedFile.Close()
15 fileContent, err := ioutil.ReadAll(uploadedFile)
16 if err != nil {
17 log.Fatal(err)
18 }
The full code for this step is available at: https://packt.live/2SkeZHW
This creates a very basic web server that receives a multipart form POST request and saves the file within the form.
package main
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"mime/multipart"
"net/http"
"os"
)
func postFileAndReturnResponse(filename string) string {
fileDataBuffer := bytes.Buffer{}
multipartWritter := multipart.NewWriter(&fileDataBuffer)
file, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
formFile, err := multipartWritter.CreateFormFile("myFile", file.Name())
if err != nil {
log.Fatal(err)
}
_, err = io.Copy(formFile, file)
if err != nil {
log.Fatal(err)
}
multipartWritter.Close()
req, err := http.NewRequest("POST", "http://localhost:8080", &fileDataBuffer)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Content-Type", multipartWritter.FormDataContentType())
response, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer response.Body.Close()
data, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
return string(data)
func main() {
data := postFileAndReturnResponse("./test.txt")
fmt.Println(data)
}
go run server.go
go run server.go
Then, if you navigate to the server directory, you should see that the test.txt file has now appeared:
In this exercise, you sent a file to a web server using the Go HTTP client. You read in a file from disk, formatted it into a POST request, and sent the data to the server.
Sometimes there is more to a request than simply requesting or sending data. This information is stored within the request headers. A very common example of this is authorization headers. When you log into a server, it will respond with an authorization token. In all future requests sent to the server, you would include this token in the request's headers so the server knows you are the one making the requests. You will learn how to add an authorization token to requests later.
In this exercise, you will create your own HTTP client and set custom options on it. You will also set an authorization token in the request headers, so the server knows it is you requesting the data:
package main
import (
"log"
"net/http"
"time"
)
type server struct{}
func (srv server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
auth := r.Header.Get("Authorization")
if auth != "superSecretToken" {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Authorization token not recognized"))
return
}
time.Sleep(10 * time.Second)
msg := "hello client!"
w.Write([]byte(msg))
}
func main() {
log.Fatal(http.ListenAndServe(":8080", server{}))
}
This creates a very basic web server that receives a request, checks the authorization header is correct, waits 10 seconds, then sends back data.
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
)
func getDataWithCustomOptionsAndReturnResponse() string {
client := http.Client{Timeout: 11 * time.Second}
req, err := http.NewRequest("POST", "http://localhost:8080", nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "superSecretToken")
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
return string(data)
func main() {
data := getDataWithCustomOptionsAndReturnResponse()
fmt.Println(data)
}
go run main.go
This will start the client and connect to the server. The client will send the request to the server and after 10 seconds it should output the following:
Note
Change the timeout settings in the client to be under 10 seconds and see what happens. You can also change or remove the authorization header on the request and see what happens.
In this exercise, you learned how to add custom headers to a request. You learned about the common example of adding an authorization header, which is required by many APIs when you want to interact with them.
Imagine you are interacting with a web API and you wish to send data to a web server. You then want to check whether the data was added. In this activity, you will do just that. You will send a POST request to the server, then request the data back using a GET request, parse the data, and print it out.
Follow these steps to get the desired outcome:
This is the expected output:
Note
The solution for this activity can be found on page 754.
In this activity, you saw how to send data to a web server using a POST request and then how to request data from the server to ensure it was updated using a GET request. Interacting with a server in this way is very common when programming professionally.
HTTP clients are used to interact with web servers. They are used to send different types of requests to a server (for example, GET or POST requests) and then react to the response returned by the server. A web browser is a type of HTTP client that will send a GET request to a web server and display the HTML data it returns. In Go, you created your own HTTP client and did the same thing, sending a GET request to https://www.google.com and then logging the response returned by the server. You also learned about the components of a URL and that you can control what you request from a server by changing the URL.
There is also more to web servers than simply requesting HTML data. You learned that they can return structured data in the form of JSON, which can be parsed and used in your code. Data can also be sent to a server using POST requests, allowing you to send form data to a server. However, the data sent to a server isn't limited to just form data: you can also upload files to a server using a POST request.
There are also ways to customize the requests you send. You learned about the common example of authorization, where you add a token to the header of HTTP requests so that a server can tell who is making that request.
In this chapter, you used some basic web servers in the exercises. However, you didn't learn about the details of what they were doing. In the next chapter, you will learn about web servers in more detail.
3.129.15.99