Working with APIs and writing web clients can be a tricky business. Different APIs have different types of authorization, authentication, and protocol. We'll explore the http.Client structure object, work with OAuth2 clients and long-term token storage, and finish off with GRPC and an additional REST interface.
By the end of this chapter, you should have an idea of how to interface with third-party or in-house APIs and have some patterns for common operations, such as async requests to APIs.
In this chapter, we will cover the following recipes:
- Initializing, storing, and passing http.Client structures
- Writing a client for a REST API
- Executing parallel and async client requests
- Making use of OAuth2 clients
- Implementing an OAuth2 token storage interface
- Wrapping a client in added functionality and function composition
- Understanding GRPC clients
- Using twitchtv/twirp for RPC
Technical requirements
In order to proceed with all the recipes in this chapter, configure your environment according to these steps:
- Download and install Go 1.12.6 or higher on youroperatingsystem athttps://golang.org/doc/install.
- Open a Terminal or console application, create a project directory such as ~/projects/go-programming-cookbook, and navigate to this directory. All code will be run and modified from this directory.
- Clone the latest code into~/projects/go-programming-cookbook-original and optionally work from that directory rather than typing the examples manually, as follows:
$ git clone [email protected]:PacktPublishing/Go-Programming-Cookbook-Second-Edition.git go-programming-cookbook-original
Initializing, storing, and passing http.Client structures
The Go net/http package exposes a flexible http.Client structure for working with HTTP APIs. This structure has separate transport functionality and makes it relatively simple to short-circuit requests, modify headers for each client operation, and handle any REST operations. Creating clients is a very common operation, and this recipe will start with the basics of working and creating an http.Client object.
How to do it...
These steps cover writing and running of your application:
- From your Terminal or console application, create a new directory called ~/projects/go-programming-cookbook/chapter7/client, and navigate to this directory.
- Run the following command:
$ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter7/client
You should see a file calledgo.mod containing the following:
module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter7/client
- Copy tests from~/projects/go-programming-cookbook-original/chapter7/client, or use this as an exercise to write some code of your own!
- Create a file called client.go with the following content:
package client
import (
"crypto/tls"
"net/http"
)
// Setup configures our client and redefines
// the global DefaultClient
func Setup(isSecure, nop bool) *http.Client {
c := http.DefaultClient
// Sometimes for testing, we want to
// turn off SSL verification
if !isSecure {
c.Transport = &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: false,
},
}
}
if nop {
c.Transport = &NopTransport{}
}
http.DefaultClient = c
return c
}
// NopTransport is a No-Op Transport
type NopTransport struct {
}
// RoundTrip Implements RoundTripper interface
func (n *NopTransport) RoundTrip(*http.Request)
(*http.Response, error) {
// note this is an uninitialized Response
// if you're looking at headers and so on.
return &http.Response{StatusCode: http.StatusTeapot}, nil
}
- Create a file called exec.go with the following content:
package client
import (
"fmt"
"net/http"
)
// DoOps takes a client, then fetches
// google.com
func DoOps(c *http.Client) error {
resp, err := c.Get("http://www.google.com")
if err != nil {
return err
}
fmt.Println("results of DoOps:", resp.StatusCode)
return nil
}
// DefaultGetGolang uses the default client
// to get golang.org
func DefaultGetGolang() error {
resp, err := http.Get("https://www.golang.org")
if err != nil {
return err
}
fmt.Println("results of DefaultGetGolang:",
resp.StatusCode)
return nil
}
- Create a file called store.go with the following content:
package client
import (
"fmt"
"net/http"
)
// Controller embeds an http.Client
// and uses it internally
type Controller struct {
*http.Client
}
// DoOps with a controller object
func (c *Controller) DoOps() error {
resp, err := c.Client.Get("http://www.google.com")
if err != nil {
return err
}
fmt.Println("results of client.DoOps", resp.StatusCode)
return nil
}
- Create a new directory called example and navigate to it.
- Create a file namedmain.gowith the following content:
package main
import "github.com/PacktPublishing/
Go-Programming-Cookbook-Second-Edition/
chapter7/client"
func main() {
// secure and op!
cli := client.Setup(true, false)
if err := client.DefaultGetGolang(); err != nil {
panic(err)
}
if err := client.DoOps(cli); err != nil {
panic(err)
}
c := client.Controller{Client: cli}
if err := c.DoOps(); err != nil {
panic(err)
}
// secure and noop
// also modifies default
client.Setup(true, true)
if err := client.DefaultGetGolang(); err != nil {
panic(err)
}
}
- Run go run main.go.
- You may also run the following command:
$ go build
$ ./example
You should now see the following output:
$ go run main.go
results of DefaultGetGolang: 200
results of DoOps: 200
results of client.DoOps 200
results of DefaultGetGolang: 418
- If you copied or wrote your own tests, go up one directory and run go test. Ensure that all the tests pass.
How it works...
The net/http package exposes a DefaultCl...