Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Hands-On RESTful Web Services with Go
Hands-On RESTful Web Services with Go

Hands-On RESTful Web Services with Go: Develop elegant RESTful APIs with Golang for microservices and the cloud , Second Edition

eBook
$20.98 $29.99
Paperback
$43.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Product feature icon AI Assistant (beta) to help accelerate your learning
Table of content icon View table of contents Preview book icon Preview Book

Hands-On RESTful Web Services with Go

Handling Routing for our REST Services

In this chapter, we will discuss routing for a REST application. To create an API, the first step is to define routes. To define routes, we have to figure out the available system packages in Go. We'll begin this chapter by exploring the basic internal routing mechanism in Go. We then see how to create a custom multiplexer, an entity that matches a given URL to a registered pattern. A multiplexer basically allows a developer to create a route to listen to client requests and attaches handlers that hold the business logic of the application. The ServeMux package is the basic multiplexer provided by Go. We'll then explore a few other frameworks as ServeMux capabilities are very limited.

This chapter also includes the likes of third-party libraries such as httprouter and gorilla/mux. Then, we'll discuss topics such as SQL injection. The crux of this chapter is to teach you how to create elegant HTTP routers in Go using gorilla/mux. We'll also briefly discuss designing a URL shortening service.

We will cover the following topics:

  • Understanding Go's net/http package
  • ServeMux—a basic router in Go
  • Understanding httprouter—a lightweight HTTP router
  • Introducing gorilla/mux—a powerful HTTP router
  • Reader's challenge: an API for URL shortening

Technical requirements

Understanding Go's net/http package

Accepting HTTP requests is the primary goal of a web server. In Go, there is a system-level package that helps developers create HTTP servers and clients. The name of the package is net/http. We can understand the functionality of the net/http package by creating a small example. The example accepts an incoming request and returns the timestamp of the server. Let us see the steps for creating such a server:

  1. Create the program file as follows:
touch -p $GOPATH/src/github.com/git-user/chapter2/healthCheck/main.go

Now, we have a file where we can develop a server with a Health Check API that returns a date/time string.

  1. Import the net/http package and create a function handler called HealthCheck. The http.HandleFunc is a method that takes a route and a function handler as its arguments. This function handler has to return an http.ResponseWriter object:
package main

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

// HealthCheck API returns date time to client
func HealthCheck(w http.ResponseWriter, req *http.Request) {
currentTime := time.Now()
io.WriteString(w, currentTime.String())
}

func main() {
http.HandleFunc("/health", HealthCheck)
log.Fatal(http.ListenAndServe(":8000", nil))
}

The preceding code creates a HealthCheck function and attaches it to an HTTP route. HandleFunc is used to attach a route pattern to a handler function. ListenAndServe starts a new HTTP server. It returns an error if the server launch is unsuccessful. It takes address:port as the first argument and the second argument is nil, which says use the default multiplexer. We will see multiplexers in detail in the upcoming sections.

Use the log function to debug potential errors. The ListenAndServe function returns an error if there is one.
  1. Now, we can start the web server using this command:
go run $GOPATH/src/github.com/git-user/chapter2/healthCheck/main.go

Run the healthCheck.go file from a shell.

  1. Now, fire up a shell or browser to see the server in action. Here, we use the curl request:
curl -X GET http://localhost:8000/health

The response is as follows:

2019-04-10 17:54:05.450783 +0200 CEST m=+6.612810181

Go has a different concept for handling request and response. We used the io library to write to the response. For web development, we can use a template to automatically fill in the details. Go's internal URL handlers use a ServeMux multiplexer. In the next section, we will discuss more on ServeMux, a built-in URL router in Go.

ServeMux – a basic router in Go

ServeMux is an HTTP request multiplexer. The HandleFunc we used in the preceding section is actually a method of ServeMux. By using ServeMux, we can handle multiple routes. We can also create our own multiplexer. A multiplexer handles the logic of separating routes with a function called ServeHTTP. So, if we create a Go struct with the ServeHTTP method, it can do the job as the in-built multiplexer.

Consider a route as a key in a Go dictionary (map) and a multiplexer as its value. Go finds the multiplexer from the route and tries to execute the ServeHTTP function. In the following section, we will see the usage of ServeMux by creating an API that generates UUID strings.

Developing a UUID generation API using ServeMux

A UUID is a unique identifier for a resource or a transaction. UUIDs are widely used for identifying an HTTP request. Let us develop an API for generating a UUID. Please follow these steps:

  1. Create the program file as follows:
touch -p $GOPATH/src/github.com/git-user/chapter2/uuidGenerator/main.go
  1. Any Go struct with a few dedicated HTTP methods is qualified to be a ServeMux. For example, we can create a custom UUID struct and implement the ServeHTTP function in order to make it a ServeMux object. Following is the implementation for the uuidGenerator.go module:
import (
"crypto/rand"
"fmt"
)

// UUID is a custom multiplexer
type UUID struct {
}

func (p *UUID) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
giveRandomUUID(w, r)
return
}
http.NotFound(w, r)
return
}

func giveRandomUUID(w http.ResponseWriter, r *http.Request) {
c := 10
b := make([]byte, c)
_, err := rand.Read(b)
if err != nil {
panic(err)
}
fmt.Fprintf(w, fmt.Sprintf("%x", b))
}

It consists of the UUID struct that acts as a ServeMux object. We can access the URL path in the handler function and use that information to manually route the requests to different response generators.

giveRandomUUID is a response generator function that sets a random UUID string to response. Go's crypto package has a Read function that fills random characters into a byte array.

  1. Now add a main function to the module using the ServeMux object. We should pass our ServeMux to the http.ListenAndServe function to get our content served. We are serving our content on port 8000:
package main

import (
"net/http"
)

func main() {
mux := &UUID{}
http.ListenAndServe(":8000", mux)
}

We use UUID as a multiplexer in the ListenAndServe function, which starts an HTTP server. The server executes the ServeHTTP method that is defined preceding on the mux object.

  1. Run the following command from your shell/Terminal:
go run $GOPATH/src/github.com/git-user/chapter2/uuidGenerator/main.go
  1. We can make a curl request like this to make a request to the web server that is listening on port 8000:
curl -X GET http://localhost:8000/

The response that is returned will be a random string:

544f5519592ac25bb2c0
Use Ctrl + C or Cmd + C to stop your Go server. If you are running it as a background process, use sudo kill `sudo lsof -t -i:8000` to kill a process running on port 8000.

Until now, we have worked with a single handler. Let us see how we can add multiple handlers to route to different function handlers using ServeMux.

Adding multiple handlers using ServeMux

Let us say we have an API requirement that generates random numbers of different types such as int, float, and so on. The custom multiplexer (mux) we developed can be cumbersome when there are multiple endpoints with different functionalities. To add that logic, we need to add multiple if/else conditions to manually check the URL route. To overcome that complex code structure, we can instantiate a new in-built ServeMux object and define many handlers. Let's look at the code with ServeMux:

newMux := http.NewServeMux()

newMux.HandleFunc("/randomFloat", func(w http.ResponseWriter,
r *http.Request) {
fmt.Fprintln(w, rand.Float64())
})

newMux.HandleFunc("/randomInt", func(w http.ResponseWriter,
r *http.Request) {
fmt.Fprintln(w, rand.Int(100))
})

This code snippet shows how to create a ServeMux and attach multiple handlers to it.

randomFloat and randomInt are the two routes we create for returning a random float and random int, respectively. Now, we pass that to the ListenAndServe function. Int(100) returns a random integer number from the range 0-100.

For more details on random functions, visit the Go random package page at: http://golang.org.

Let us see a complete example:

  1. Create a file to hold our program and call it multipleHandlers.go in the following path:
touch -p $GOPATH/src/github.com/git-user/chapter2/multipleHandlers/main.go
  1. Now create a main function and add the code for creating the ServeMux object and function handlers.
  2. Finally, run the server with the http.ListenAndServe method:
package main

import (
"fmt"
"math/rand"
"net/http"
)

func main() {
newMux := http.NewServeMux()
newMux.HandleFunc("/randomFloat", func(w http.ResponseWriter,
r *http.Request) {
fmt.Fprintln(w, rand.Float64())
})
newMux.HandleFunc("/randomInt", func(w http.ResponseWriter,
r *http.Request) {
fmt.Fprintln(w, rand.Intn(100))
})
http.ListenAndServe(":8000", newMux)
}
  1. We can run the program directly using the run command:
go run $GOPATH/src/github.com/git-user/chapter2/multipleHandlers/main.go
  1. Now, let us fire two curl commands and see the output:
curl -X GET http://localhost:8000/randomFloat
curl -X GET http://localhost:8000/randomInt

The responses will be:

0.6046602879796196
87

We saw how we can create a URL router with basic Go constructs. Let us have a look at a few popular URL routing frameworks that are widely used by the Go community for their API servers.

Understanding httprouter – a lightweight HTTP router

httprouter, as the name suggests, routes the HTTP requests to particular handlers. httprouter is a well-known package in Go for creating simple routers with an elegant API. The developers coming from the Python/Django community are very familiar with a full-blown URL dispatcher in the Django framework. httprouter provides similar features:

  • Allows variables in the route paths
  • Matches the REST methods (GET, POST, PUT, and so on)
  • No compromise of performance

We are going to discuss these qualities in more detail in the following section. Before that, there are a few noteworthy points that make httprouter an even better URL router:

  • httprouter plays well with the in-built http.Handler
  • httprouter explicitly says that a request can only match to one route or no route
  • The router's design encourages building sensible, hierarchical RESTful APIs
  • You can build simple and efficient static file servers

In the next section, we see the installation of httprouter and its basic usage.

Installing httprouter

httprouter is an open source Go package and can be installed using the go get command. Let us see the installation and basic usage in the steps given as follows:

  1. Install httprouter using this command:
go get github.com/julienschmidt/httprouter

We can import the library in our source code, like this:

import "github.com/julienschmidt/httprouter"
  1. The basic usage of httprouter can be understood through an example.
    Let us write a REST service in Go that provides two things:
  • Gets the Go compiler version
  • Gets the content of a given file

We need to use a system package called os/exec to fetch the preceding details.

  1. The os/exec package has a Command function, using which we can make any system call and the function signature is this:
// arguments... means an array of strings unpacked as arguments
// in Go

cmd := exec.Command(command, arguments...)
  1. The exec.Command function takes the command and an additional argument's array. Additional arguments are the options or input for the command. It can then be executed by calling the Output function, like this:
out, err := cmd.Output()
  1. This program uses httprouter to create the service. Let us create it at the following path:
touch -p $GOPATH/src/github.com/git-user/chapter2/httprouterExample/main.go

The program's main function creates two routes and two function handlers. The responsibilities of function handlers are:

  • To get the current Go compiler version
  • To get the contents of a file

The program is trying to implement a REST service using httprouter. We are defining two routes here:

  • /api/v1/go-version
  • /api/v1/show-file/:name
package main

import (
"fmt"
"io"
"log"
"net/http"
"os/exec"

"github.com/julienschmidt/httprouter"
)


func main() {
router := httprouter.New()
router.GET("/api/v1/go-version", goVersion)
router.GET("/api/v1/show-file/:name", getFileContent)
log.Fatal(http.ListenAndServe(":8000", router))
}

:name is a path parameter. The basic Go router cannot define these special parameters. By using httprouter, we can match the REST methods. In the main block, we are matching GET requests to their respective routes.

Now we are coming to the implementation of three handler functions:

func getCommandOutput(command string, arguments ...string) string {
out, _ := exec.Command(command, arguments...).Output()
return string(out)
}

func goVersion(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
response := getCommandOutput("/usr/local/go/bin/go", "version")
io.WriteString(w, response)
return
}

func getFileContent(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
fmt.Fprintf(w, getCommandOutput("/bin/cat", params.ByName("name")))
}

exec.Command takes the bash command and respective options as its arguments and returns an object. That object has an Output method that returns the output result of command execution. We are utilizing this utility getCommandOutput function in both goVersion and getFileContent handlers. We use shell command formats such as go --version and cat file_name in handlers.

If you observe the code, we used /usr/local/go/bin/go as the Go executable location because it is the Go compiler location in Mac OS X. While executing exec.Command, you should give the absolute path of the executable. So, if you are working on an Ubuntu machine or Windows, use the path to your installed Go executable. On Linux machines, you can easily find that out by using the $ which go command.

Now create two new files in the same directory. These files will be served by our file server program. You can create any custom files in this directory for testing:

Latin.txt:

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.

Greek.txt:

Οἱ δὲ Φοίνιϰες οὗτοι οἱ σὺν Κάδμῳ ἀπιϰόμενοι.. ἐσήγαγον διδασϰάλια ἐς τοὺς ῞Ελληνας ϰαὶ δὴ ϰαὶ γράμματα, οὐϰ ἐόντα πρὶν ῞Ελλησι ὡς ἐμοὶ δοϰέειν, πρῶτα μὲν τοῖσι ϰαὶ ἅπαντες χρέωνται Φοίνιϰες· μετὰ δὲ χρόνου προβαίνοντος ἅμα τῇ ϕωνῇ μετέβαλον ϰαὶ τὸν ϱυϑμὸν τῶν γραμμάτων. Περιοίϰεον δέ σϕεας τὰ πολλὰ τῶν χώρων τοῦτον τὸν χρόνον ῾Ελλήνων ῎Ιωνες· οἳ παραλαβόντες διδαχῇ παρὰ τῶν Φοινίϰων τὰ γράμματα, μεταρρυϑμίσαντές σϕεων ὀλίγα ἐχρέωντο, χρεώμενοι δὲ ἐϕάτισαν, ὥσπερ ϰαὶ τὸ δίϰαιον ἔϕερε ἐσαγαγόντων Φοινίϰων ἐς τὴν ῾Ελλάδα, ϕοινιϰήια ϰεϰλῆσϑαι.

Now run the program with this command. This time, instead of firing a curl command, let us use the browser as our output for GET. Windows users may not have curl as the first-hand application. They can use API testing software such as the Postman client while developing the REST API. Take a look at the following command:

go run $GOPATH/src/github.com/git-user/chapter2/httprouterExample/main.go

The output for the first GET request looks like this:

curl -X GET http://localhost:8000/api/v1/go-version

The result will be this:

go version go1.13.5 darwin/amd64

The second GET request requesting Greek.txt is:

curl -X GET http://localhost:8000/api/v1/show-file/greek.txt

Now, we will see the file output in Greek:

Οἱ δὲ Φοίνιϰες οὗτοι οἱ σὺν Κάδμῳ ἀπιϰόμενοι.. ἐσήγαγον διδασϰάλια ἐς τοὺς ῞Ελληνας ϰαὶ δὴ ϰαὶ γράμματα, οὐϰ ἐόντα πρὶν ῞Ελλησι ὡς ἐμοὶ δοϰέειν, πρῶτα μὲν τοῖσι ϰαὶ ἅπαντες χρέωνται Φοίνιϰες· μετὰ δὲ χρόνου προβαίνοντος ἅμα τῇ ϕωνῇ μετέβαλον ϰαὶ τὸν ϱυϑμὸν τῶν γραμμάτων. Περιοίϰεον δέ σϕεας τὰ πολλὰ τῶν χώρων τοῦτον τὸν χρόνον ῾Ελλήνων ῎Ιωνες· οἳ παραλαβόντες διδαχῇ παρὰ τῶν Φοινίϰων τὰ γράμματα, μεταρρυϑμίσαντές σϕεων ὀλίγα ἐχρέωντο, χρεώμενοι δὲ ἐϕάτισαν, ὥσπερ ϰαὶ τὸ δίϰαιον ἔϕερε ἐσαγαγόντων Φοινίϰων ἐς τὴν ῾Ελλάδα, ϕοινιϰήια ϰεϰλῆσϑαι.
Never give the user the power to execute system commands over the REST API. In the exec example, we made handlers use a getCommandOutput helper function to execute system commands.

The endpoint /api/v1/show-file/ we defined in the exec example is not so efficient. Using httprouter, we can build advanced and performance-optimized file servers. In the next section, we'll learn how to do that.

Building a simple static file server in minutes

Sometimes, an API can serve files. The other application of httprouter, apart from routing, is building an efficient file server. It means that we can build a content delivery platform of our own. Some clients need static files from the server. Traditionally, we use Apache2 or Nginx for that purpose. If one has to create something similar purely in Go, they can leverage httprouter.

Let us build one. From the Go server, in order to serve the static files, we need to route them through a universal route, like this:

/static/*

The plan is to use http package's Dir method to load the filesystem, and pass filesystem handler it returns to httprouter. We can use the ServeFiles function of the httprouter instance to attach a router to the filesystem handler. It should serve all the files in the given public directory. Usually, static files are kept in the /var/public/www folder on a Linux machine. Create a folder called static in your home directory:

mkdir -p /users/git-user/static

Now, copy the Latin.txt and Greek.txt files, which we created for the previous example, to the preceding static directory. After doing that, let us write the program for the file server using the following steps. You will be amazed at the simplicity of httprouter:

  1. Create a program at the following path:
touch -p $GOPATH/src/github.com/git-user/chapter2/fileServer/main.go
  1. Update the code like the following. You have to add a route that links a static file path route to a filesystem handler:
package main

import (
"log"
"net/http"

"github.com/julienschmidt/httprouter"
)

func main() {
router := httprouter.New()
// Mapping to methods is possible with HttpRouter
router.ServeFiles("/static/*filepath",
http.Dir("/Users/git-user/static"))
log.Fatal(http.ListenAndServe(":8000", router))
}

  1. Now run the server and see the output:
go run $GOPATH/src/github.com/git-user/chapter2/fileServer/main.go
  1. Open another Terminal and fire this curl request:
http://localhost:8000/static/latin.txt
  1. Now, the output will be a static file content server from our file server:
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.

In the next section, we discuss about a widely used HTTP router called gorilla/mux.

Introducing gorilla/mux – a powerful HTTP router

The word Mux stands for the multiplexer. gorilla/mux is a multiplexer designed to multiplex HTTP routes (URLs) to different handlers. Handlers are the functions that can handle the given requests. gorilla/mux is a wonderful package for writing beautiful routes for our API servers.

gorilla/mux provides tons of options to control how routing is done to your web application. It allows a lot of features, such as:

  • Path-based matching
  • Query-based matching
  • Domain-based matching
  • Sub-domain-based matching
  • Reverse URL generation

Which type of routing to use depends on the types of clients requesting the server. We first see the installation and then a basic example to understand the gorilla/mux package.

Installing gorilla/mux

Follow these steps to install the mux package:

  1. You need to run this command in the Terminal (Mac OS X and Linux):
go get -u github.com/gorilla/mux
  1. If you get any errors saying package github.com/gorilla/mux: cannot download, $GOPATH not set. For more details see--go help gopath, set the $GOPATH environment variable using the following command:
export GOPATH=~/go
  1. As we discussed in Chapter 1, Getting Started with REST API Development, all the packages and programs go into GOPATH. It has three folders: bin, pkg, and src. Now, add GOPATH to the PATH variable to use the installed bin files as system utilities that have no ./executable style. Refer to the following command:
PATH="$GOPATH/bin:$PATH"
  1. These settings stay until you turn off your machine. So, to make it a permanent change, add the preceding line to your bash/zsh profile file:
vi ~/.profile
(or)
vi ~/.zshrc

We can import gorilla/mux in our programs, like this:

import "github.com/gorilla/mux"

Now, we are ready to go. Assuming gorilla/mux is installed, we can now explore its basics.

Fundamentals of gorilla/mux

The gorilla/mux package primarily helps to create routers, similar to httprouter. The difference between both is the attachment of a handler function to a given URL. If we observe, the gorilla/mux way of attaching a handler is similar to that of basic ServeMux. Unlike httprouter, gorilla/mux wraps all the information of an HTTP request into a single request object.

The three important tools provided in the gorilla/mux API are:

  • The mux.NewRouter method
  • The *http.Request object
  • The *http.ResponseWriter object

The NewRouter method creates a new router object. That object basically maps a route to a function handler. gorilla/mux passes a modified *http.Request and *http.ResponseWriter object to the function handler. These special objects have lots of additional information about headers, path parameters, request body, and query parameters. Let us explain how to define and use different routers in gorilla/mux with two common types:

  • Path-based matching
  • Query-based matching

Path-based matching

A path parameter in the URL of an HTTP GET request looks like this:

https://example.org/articles/books/123

Since it is passed after the base URL and API endpoint, in this case https://example.org/articles/, they are called path parameters. In the preceding URL, books and 123 are path parameters. Let us see an example of how to create routes that can consume data supplied as path parameters. Follow these steps:

  1. Create a new file for our program at the following path:
touch -p $GOPATH/src/github.com/git-user/chapter2/muxRouter/main.go
  1. The idea is to create a new router, mux.NewRouter, and use it as a handler with in-built http.Server. We can attach URL endpoints to handler functions on this router object. The URL endpoints attached can also be regular expressions. The simple program to collect path parameters from a client HTTP request and return back the same looks like this:
package main

import (
"fmt"
"log"
"net/http"
"time"

"github.com/gorilla/mux"
)

func ArticleHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Category is: %v\n", vars["category"])
fmt.Fprintf(w, "ID is: %v\n", vars["id"])
}

func main() {
r := mux.NewRouter()
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
srv := &http.Server{
Handler: r,
Addr: "127.0.0.1:8000",
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}
  1. Now run the server using the following command in a shell:
go run $GOPATH/src/github.com/git-user/chapter2/muxRouter/main.go
  1. Make a curl request from another shell and we can get the output as follows:
curl http://localhost:8000/articles/books/123

Category is: books ID is: 123

This example shows how to match and parse path parameters. There is one more popular way to collect variable information from an HTTP request and that is with query parameters. In the next section, we see how to create routes that match HTTP requests with query parameters.

Query-based matching

Query parameters are variables that get passed along with the URL in an HTTP request. This is what we commonly see in a REST GET request. The gorilla/mux route can match and collect query parameters. See this following URL, for example:

http://localhost:8000/articles?id=123&category=books

It has id and category as query parameters. All query parameters begin after the ? character.

Let us modify our copy of our previous example into a new one with the name queryParameters/main.go. Modify the route object to point to a new handler called QueryHandler, like this:

// Add this in your main program
r := mux.NewRouter()
r.HandleFunc("/articles", QueryHandler)

In QueryHandler, we can use request.URL.Query() to obtain query parameters from the HTTP request. QueryHandler looks like this:

// QueryHandler handles the given query parameters
func QueryHandler(w http.ResponseWriter, r *http.Request) {
queryParams := r.URL.Query()
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Got parameter id:%s!\n", queryParams["id"][0])
fmt.Fprintf(w, "Got parameter category:%s!", queryParams["category"][0])
}

This program is similar to the previous example, but processes query parameters instead of path parameters.

Run the new program:

go run $GOPATH/src/github.com/git-user/chapter2/queryParameters/main.go

Fire a curl request in this format in a Terminal:

curl -X GET http://localhost:8000/articles\?id\=1345\&category\=birds

We need to escape special characters in the shell. If it is in the browser, there is no problem of escaping. The output looks like this:

Got parameter id:1345! 
Got parameter category:birds!

The r.URL.Query() function returns a map with all the parameter and value pairs. They are basically strings and, in order to use them in our program logic, we need to convert the number strings to integers. We can use Go's strconv package to convert a string to an integer, and vice versa.

We have used http.StatusOK to write a successful HTTP response. Similarly, use appropriate status codes for different REST operations. For example, 404 – Not found, 500 – Server error, and so on.

Other notable features of gorilla/mux

We have seen two basic examples. What next? The gorilla/mux package provides many handy features that makes an API developer's life easy. It gives a lot of flexibility while creating routes. In this section, we try to discuss a few important features. The first feature of interest is generating a dynamic URL with the reverse mapping technique.

In simple words, reverse mapping a URL is getting the complete API route for an API resource. Reverse mapping is quite useful when we share links to our web application or API. However, in order to create a URL from data, we should associate a Name with the gorilla/mux route. You can name a multiplexer route, like this:

r.HandlerFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
Name("articleRoute")

Now, we can get a dynamically generated API route by using the url method:

url, err := r.Get("articleRoute").URL("category", "books", "id", "123")
fmt.Printf(url.Path) // prints /articles/books/123

If a route consists of the additional path parameters defined, we should pass that data as arguments to the URL method.

The next important feature of a URL router is path prefix. A path prefix is a wildcard route for matching all possible paths. It matches all the routes to the API server, post a root word. The general use case of path prefixes is a static file server. Then, when we serve files from a static folder, the API paths should match to filesystem paths to successfully return file content.

For example, if we define /static/ as a path prefix, every API route that has this root word as a prefix is routed to the handler attached.

These paths are matched:

  • http://localhost:8000/static/js/jquery.min.js
  • http://localhost:8000/static/index.html
  • http://localhost:8000/static/some_file.extension

Using gorilla/mux's PathPrefix and StripPefix methods, we can write a static file server, like this:

r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("/tmp/static"))))

The next important feature is strict slash. A strict slash activated on a gorilla/mux router allows a URL to redirect to the same URL with / appended at the end and vice versa.

For example, let us say we have an /articles/ route that is attached to an ArticleHandler handler:

r.StrictSlash(true)
r.Path("/articles/").Handler(ArticleHandler)

In the preceding case, strict slash is set to true. The router then redirects even /articles (without '/' at the end) to the ArticleHandler. If it is set to false, the router treats both /articles/ and /articles as different paths.

The next important feature of a URL router is to match encoded path parameters. The gorilla/mux UseEncodedPath method can be called on a router to match encoded path parameters.

A server can receive encoded paths from a few clients. We can match the encoded path parameter, we can even match the encoded URL route and forward it to the given handler:

r.UseEncodedPath()
r.NewRoute().Path("/category/id")

This can match the following URL:

http://localhost:8000/books/2

As well as this:

http://localhost:8000/books%2F2

Where %2F2 stands for /2 in encoded form.

Its pattern-matching features and simplicity push gorilla/mux as a popular choice for an HTTP router in projects. Many successful projects worldwide are already using mux for their routing needs.

We are free to define routes for our application. Since routes are entry points to any API, developers should be careful about how they process the data received from a client. Clients can be attackers too, who can inject malicious scripts into the path or query parameters. That situation is called a security vulnerability. APIs are prone to a common application vulnerability called SQL injection. In the next section, we introduce it briefly and see possible countermeasure steps.

SQL injection in URLs and ways to avoid them

SQL injection is a process of attacking a database with malicious scripts. If one is not careful when defining URL routes, there may be an opportunity for SQL injection. These attacks can happen for all kinds of REST operations. For example, if we are allowing the client to pass parameters to the server, then there is a chance for an attacker to append an ill-formed string to those parameters. If we are using those variables/parameters directly into an SQL query executing on our database, it could lead to a potential vulnerability.

Look at the following Go code snippet that inserts username and password details into the database. It collects values from an HTTP POST request and appends raw values to the SQL query:

username := r.Form.Get("id")
password := r.Form.Get("category")
sql := "SELECT * FROM article WHERE id='" + username + "' AND category='" + password + "'"
Db.Exec(sql)

In the snippet, we are executing a database SQL query, but since we are appending the values directly, we may include malicious SQL statements such as -- comments and ORDER BY n range clauses in the query:

?category=books&id=10 ORDER BY 10--

If the application returns the database response directly to the client, it can leak information about the columns the table has. An attacker can change the ORDER BY to another number and extract sensitive information:

Unknown column '10' in 'order clause'

We will see more about this in our upcoming chapters where we build fully-fledged REST services with other methods, such as POST, PUT, and so on:

Now, how to avoid these injections. There are several precautions:

  • Set the user level permissions to various tables in the database
  • Log the requests and find the suspicious ones
  • Use the HTMLEscapeString function from Go's text/template package to escape special characters in the API parameters, such as body and path
  • Use a driver program instead of executing raw SQL queries
  • Stop relaying database debug messages back to the client
  • Use security tools such as sqlmap to find out vulnerabilities

With the basics of routing and security covered, in the next section we present an interesting challenge for the reader. It is to create a URL shortening service. We provide all the background details briefly in the next section.

Reader's challenge – an API for URL shortening

With all the basics you have learned up to now, try to implement a URL shortening service. A URL shortener takes a very long URL and returns a shortened, crisp, and memorable URL back to the user. At first sight, it looks like magic, but it is a simple math trick.

In a single statement, URL shortening services are built upon two things:

  • A string mapping algorithm to map long strings to short strings (Base 62)
  • A simple web server that redirects a short URL to the original URL

There are a few obvious advantages of URL shortening:

  • Users can remember the URL; easy to maintain
  • Users can use the links where there are restrictions on text length, for example, Twitter
  • Predictable shortened URL length

Take a look at the following diagram:

Under the hood, the following things happen in a URL shortening service:

  1. Take the original URL
  2. Apply BASE62 encoding on it; it generates a Shortened URL
  3. Store that URL in the database. Map it to the original URL ([shortened_url: original_url])
  4. Whenever a request comes to the shortened URL, just do an HTTP redirect to the original URL

We will implement a full example in upcoming chapters when we integrate databases to our API server, but before that, though, we should specify the API design documentation.

Take a look at the following table:

URL REST Verb Action Success Failure
/api/v1/new POST Create a shortened URL 200 500, 404
/api/v1/:url GET Redirect to original URL 301 404
You can use a dummy JSON file/Go map to store the URL for now instead of a database.

Summary

In this chapter, we first introduced the HTTP router. We tried to create HTTP routes using Go's net/http package. Then, we briefly discussed ServeMux with an example. We saw how to add multiple handler functions to multiple routes. Then, we introduced a lightweight router package called httprouter, which allows developers to create elegant routes, with the option of parsing parameters passed in the URL path.

We can also serve files over the HTTP using httprouter. We built a small service to get the Go version and file contents (read-only). That example can be extended to fetch any system information or run a system command.

Next, we introduced the popular Go routing library, gorilla/mux. We discussed how it is different from httprouter and explored its functionality by implementing two examples. We explained how Vars can be used to get path parameters and r.URL.Query to parse query parameters.

As part of securing API routes, we discussed SQL injection and how it can happen in our applications. We have also seen the counter measures. By the end of this chapter, one can define routes and handler functions to accept HTTP API requests.

In the next chapter, we will look at Middleware functions, which act as tamperers for HTTP requests and responses. That phenomenon helps us to modify the API response on the fly. The next chapter also features Remote Procedure Call (RPC).

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Employ a combination of custom and open source solutions for application program interface (API) development
  • Discover asynchronous API and API security patterns and learn how to deploy your web services to the cloud
  • Apply design patterns and techniques to build reactive and scalable web services

Description

Building RESTful web services can be tough as there are countless standards and ways to develop API. In modern architectures such as microservices, RESTful APIs are common in communication, making idiomatic and scalable API development crucial. This book covers basic through to advanced API development concepts and supporting tools. You’ll start with an introduction to REST API development before moving on to building the essential blocks for working with Go. You’ll explore routers, middleware, and available open source web development solutions in Go to create robust APIs, and understand the application and database layers to build RESTful web services. You’ll learn various data formats like protocol buffers and JSON, and understand how to serve them over HTTP and gRPC. After covering advanced topics such as asynchronous API design and GraphQL for building scalable web services, you’ll discover how microservices can benefit from REST. You’ll also explore packaging artifacts in the form of containers and understand how to set up an ideal deployment ecosystem for web services. Finally, you’ll cover the provisioning of infrastructure using infrastructure as code (IaC) and secure your REST API. By the end of the book, you’ll have intermediate knowledge of web service development and be able to apply the skills you’ve learned in a practical way.

Who is this book for?

This book is for all the Go developers who are comfortable with the language and seeking to learn REST API development. Even senior engineers can enjoy this book, as it discusses many cutting-edge concepts, such as building microservices, developing API with GraphQL, using protocol buffers, asynchronous API design, and Infrastructure as a Code. Developers who are already familiar with REST concepts and stepping into the Go world from other platforms, such as Python and Ruby, can also benefit a lot.

What you will learn

  • Explore the fundamentals of API development and web services
  • Understand the various building blocks of API development in Go
  • Use superior open source solutions for representational state transfer (REST) API development
  • Scale a service using microservices and asynchronous design patterns
  • Deliver containerized artifacts to the Amazon Web Services (AWS) Cloud
  • Get to grips with API security and its implementation
Estimated delivery fee Deliver to Turkey

Standard delivery 10 - 13 business days

$12.95

Premium delivery 3 - 6 business days

$34.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Feb 28, 2020
Length: 404 pages
Edition : 2nd
Language : English
ISBN-13 : 9781838643577
Vendor :
Google
Languages :
Concepts :

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Product feature icon AI Assistant (beta) to help accelerate your learning
Estimated delivery fee Deliver to Turkey

Standard delivery 10 - 13 business days

$12.95

Premium delivery 3 - 6 business days

$34.95
(Includes tracking information)

Product Details

Publication date : Feb 28, 2020
Length: 404 pages
Edition : 2nd
Language : English
ISBN-13 : 9781838643577
Vendor :
Google
Languages :
Concepts :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
$199.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts
$279.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total $ 147.97
Hands-On High Performance with Go
$48.99
Hands-On RESTful Web Services with Go
$43.99
Hands-On Software Engineering with Golang
$54.99
Total $ 147.97 Stars icon

Table of Contents

15 Chapters
Getting Started with REST API Development Chevron down icon Chevron up icon
Handling Routing for our REST Services Chevron down icon Chevron up icon
Working with Middleware and RPC Chevron down icon Chevron up icon
Simplifying RESTful Services with Popular Go Frameworks Chevron down icon Chevron up icon
Working with MongoDB and Go to Create a REST API Chevron down icon Chevron up icon
Working with Protocol Buffers and gRPC Chevron down icon Chevron up icon
Working with PostgreSQL, JSON, and Go Chevron down icon Chevron up icon
Building a REST API Client in Go Chevron down icon Chevron up icon
Asynchronous API Design Chevron down icon Chevron up icon
GraphQL and Go Chevron down icon Chevron up icon
Scaling our REST API Using Microservices Chevron down icon Chevron up icon
Containerizing REST Services for Deployment Chevron down icon Chevron up icon
Deploying REST Services on Amazon Web Services Chevron down icon Chevron up icon
Handling Authentication for our REST Services Chevron down icon Chevron up icon
Other Books You May Enjoy Chevron down icon Chevron up icon

Customer reviews

Rating distribution
Full star icon Half star icon Empty star icon Empty star icon Empty star icon 1.8
(4 Ratings)
5 star 0%
4 star 25%
3 star 0%
2 star 0%
1 star 75%
E. Leonard Aug 13, 2021
Full star icon Full star icon Full star icon Full star icon Empty star icon 4
One of the most enjoyable books I have read for a long time. I coded 43 projects while working through this book, which indicates that they are quite short, concise and aimed at driving home one or two concepts at a time. I found the writing comfortable and it covers most if not all of the architecture patterns you’re likely to encounter in a working capacity. The book is from a couple of years ago so some packages in the book have been updated and the code needs some easily googleable tweaks to work with the current package versions as of Q3 2021. This is a book that will give you a good repository of starter projects and boilerplates for your own projects. If you know the Go language and fancy learning some web services development beyond the standard library net/http packages or Gorilla then this will give you a great intro to common API and microservices approaches. An easy 4.5 / 5
Amazon Verified review Amazon
Raj Feb 09, 2021
Full star icon Empty star icon Empty star icon Empty star icon Empty star icon 1
Very disappointing experience of buying this book. Wish I can get my money back. Feels like author googled few libraries and put together some very basic code. A better explanation and code can be found easily on google. Really very disappointing for me.
Amazon Verified review Amazon
sanoof Oct 09, 2020
Full star icon Empty star icon Empty star icon Empty star icon Empty star icon 1
only basics and outdated
Amazon Verified review Amazon
Roberto J. Yudice Dec 22, 2020
Full star icon Empty star icon Empty star icon Empty star icon Empty star icon 1
It seems like this author was trying in every possible way to extend the length of the book. To be fair that seems to be the case with most programming books today. The book is supposed to be about designing services yet you will often spend time setting up database data. It would had been better to just start with a base code that already has the database setup and focus on writing the restful services which is suppose to be the book's focus. It seems the author ran out of words and was trying to find a way to make the book longer.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela