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

You're reading from   Hands-On RESTful Web Services with Go Develop elegant RESTful APIs with Golang for microservices and the cloud

Arrow left icon
Product type Paperback
Published in Feb 2020
Publisher Packt
ISBN-13 9781838643577
Length 404 pages
Edition 2nd Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Naren Yellavula Naren Yellavula
Author Profile Icon Naren Yellavula
Naren Yellavula
Arrow right icon
View More author details
Toc

Table of Contents (16) Chapters Close

Preface 1. Getting Started with REST API Development 2. Handling Routing for our REST Services FREE CHAPTER 3. Working with Middleware and RPC 4. Simplifying RESTful Services with Popular Go Frameworks 5. Working with MongoDB and Go to Create a REST API 6. Working with Protocol Buffers and gRPC 7. Working with PostgreSQL, JSON, and Go 8. Building a REST API Client in Go 9. Asynchronous API Design 10. GraphQL and Go 11. Scaling our REST API Using Microservices 12. Containerizing REST Services for Deployment 13. Deploying REST Services on Amazon Web Services 14. Handling Authentication for our REST Services 15. Other Books You May Enjoy

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.

You have been reading a chapter from
Hands-On RESTful Web Services with Go - Second Edition
Published in: Feb 2020
Publisher: Packt
ISBN-13: 9781838643577
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime