Saturday, November 21, 2015

Upgrade Your Appengine Tests with Testify

Go's standard testing library provides a simple but powerful interface for testing your Go code, and since your tests are just regular old Go code, not only does that make the tests easy to write, but it also makes it it easy for people to see how to use your APIs in their Go code. Also, with the net/http/httptest library it's easy to simulate HTTP requests to your web apps and make sure you're getting the correct responses.

However, if you got into Go from Node.js like I did, one thing you might miss is being able to easily write tests with assert syntax like in Chai and get rid of boilerplate or preparing your tests with beforeEach functions in Mocha and Jasmine. Luckily, there's a set of Go libraries for adding that functionality to your tests in Go, Testify Assert, Require, and Suite, and in this tutorial, I'll show you how to add that functionality to upgrade a test for an appengine app, which is how I have used these libraries writing tests on the job at Meta!

The code we're testing

The handler we're going to test is a handler for adding a map location to the appengine datastore, which has a name, a latitude coordinate, and a longitude coordinate, which are passed in on a JSON request body.
type Location struct {
    Name string
    Lat  float64
    Lng  float64
}

func addLocation(w http.ResponseWriter, r *http.Request) {
    // Create a new appengine context for the request
    ctx := appengine.NewContext(r)

    // Prepare a datastore key for the location we're adding
    locationKey := datastore.NewIncompleteKey(ctx, "Location", nil)

    // Read in the request body and unmarshal its JSON into a
    // Location struct
    var loc Location
    reqBody, err := ioutil.ReadAll(r.Body)
    if err != nil {
        w.Write([]byte(fmt.Sprintf(`{"Error":"%v"}`, err)))
        return
    }
    err = json.Unmarshal(reqBody, &loc)
    if err != nil {
        w.Write([]byte(fmt.Sprintf(`{"Error":"%v"}`, err)))
        return
    }

    // Add that Location struct to the datastore. If no error
    // occurs, send a JSON response indicating the insertion
    // was successful.
    _, err = datastore.Put(ctx, locationKey, &loc)
    if err != nil {
        w.Write([]byte(fmt.Sprintf(`{"Error":"%v"}`, err)))
        return
    }
    w.Write([]byte(`{"addLocation":"success"}`))
}
For those of you who have never tried Googe Appengine in Go, which you can do here, this is how addLocation works. It gets an HTTP request with a JSON encoding of a Location as the request body, parses the body into a Location struct, and then adds the struct to the appengine datastore. If there's any error along the way, the response is an error message. Otherwise, it's a message saying the insertion was successful. So if we send in a request with the body

{"Name":"Cambridge Fresh Pond", "Lat":42.385658, "Lng":-71.149308}


The Cambridge Fresh Pond would be added to the datastore and we would get the response

{"addLocation":"success"}


But if we sent a request with broken JSON like:

{"Name":"Cambridge Fresh Pond", "Lat":42.385658, "Lng":-71.149308


We would get an error message like

{"Error":"unexpected end of JSON input"}


Simple enough. Now let's add it to our Gorilla Mux router.
func initRouter() *mux.Router {
    rt := mux.NewRouter()
    rt.HandleFunc("/add-location", addLocation)

    // Add the other routes to the router here

    return rt
}

Now let's see how we would test this addLocation handler in our router!

Testing with the standard testing library


The test we are running to test addLocation is to send a request to the /add-location route with a Location in JSON as the request body and then look into the datastore to make sure that a location was added. For this test, in order to make HTTP requests that are valid for accessing the datastore, we will be using the appengine aetest library to create an appengine test instance, and to send the requests, we will be using the router from initRouter.

func TestAddLocation(t *testing.T) {
    inst, err := aetest.NewInstance(
        &aetest.Options{StronglyConsistentDatastore: true})
    if err != nil {
        t.Fatalf("Error creating aetest instance: %v", err)
    }
    defer inst.Close()
    rt := initRouter()

    loc, err := json.Marshal(&Location{
        Name: "Cambridge Fresh Pond",
        Lat:  42.385658,
        Lng:  -71.149308,
    })
    if err != nil {
        t.Errorf("Error marshalling Location into JSON: %v", err)
    }

    req, err := inst.NewRequest("POST", "/add-location",
        ioutil.NopCloser(bytes.NewBuffer(loc)))
    if err != nil {
        t.Fatalf("Error preparing request: %v", err)
    }
    rec := httptest.NewRecorder()
    rt.ServeHTTP(rec, req)
    const expectedResponse = `{"addLocation":"success"}`
    if string(rec.Body.Bytes()) != expectedResponse {
        t.Errorf("Expected response to be %s, got %s",
            expectedResponse, string(rec.Body.Bytes()))
    }

    dbReq, err := inst.NewRequest("GET", "/", nil)
    if err != nil {
        t.Fatalf("Error preparing request: %v", err)
    }
    ctx := appengine.NewContext(dbReq)

    q := datastore.NewQuery("Location")
    numLocs, err := q.Count(ctx)
    if err != nil {
        t.Fatalf("Error preparing request: %v", err)
    }
    if numLocs != 1 {
        t.Errorf("Expected number of locations to be 1, got %d", numLocs)
    }
}
As you can see, we have a lot of code here. Let's break this down:

inst, err := aetest.NewInstance(
    &aetest.Options{StronglyConsistentDatastore: true})
if err != nil {
    t.Fatalf("Error creating aetest instance: %v", err)
}
defer inst.Close()

rt := initRouter()
Here, we make our appengine test instance (which we use for accessing appengine functionality) and our HTTP router. To make sure our test instance always closes at the end of the test, we defer its closing. 

loc, err := json.Marshal(&Location{
    Name: "Cambridge Fresh Pond",
    Lat:  42.385658,
    Lng:  -71.149308,
})
if err != nil {
    t.Errorf("Error marshalling Location into JSON: %v", err)
}
For our test we are putting a Location in the datastore, so here we make one in JSON.

req, err := inst.NewRequest("POST", "/add-location",
    ioutil.NopCloser(bytes.NewBuffer(loc)))
if err != nil {
    t.Fatalf("Error preparing request: %v", err)
}
rec := httptest.NewRecorder()
rt.ServeHTTP(rec, req)

const expectedResponse = `{"addLocation":"success"}`
if string(rec.Body.Bytes()) != expectedResponse {
    t.Errorf("Expected response to be %s, got %s",
        expectedResponse, string(rec.Body.Bytes()))
}
Then we make and send a POST request to /add-location with our Location as the request body, checking to make sure we get a success message as our response. 

dbReq, err := inst.NewRequest("GET", "/", nil)
if err != nil {
    t.Fatalf("Error preparing request: %v", err)
}
ctx := appengine.NewContext(dbReq)
Then we make a new HTTP request on this appengine instance and get its context. This request never gets sent; rather it is only made for getting an appengine context that can be used for accessing the datastore.

q := datastore.NewQuery("Location")
numLocs, err := q.Count(ctx)
if err != nil {
    t.Fatalf("Error preparing request: %v", err)
}
if numLocs != 1 {
    t.Errorf("Expected number of locations to be 1, got %d", numLocs)
}
Finally, we query for the number of Locations in the datastore, checking to confirm that one was added. 

While the test works, it presents one issue: we use a lot of if statements as assertions, each taking up three lines. In Node.js or Ruby on Rails testing libraries, we would be able to do something like expect(numLocs).to.equal(1) for a one-line assertion. This is where Testify has our back!


Concise assertions with Testify Assert and Require

As I mentioned, one issue with the assertions we have is they are one-liner if statements. With Testify Assert, you can take an assertion like
if err != nil {
    t.Errorf("Error creating aetest instance, got %v", err)
}

And convert it to:
assert.Nil(t, err, "Error creating aetest instance, got %v", err)

Or if that error is a showstopper where you need to end the test right away, instead of doing an if statement with t.Fatalf, you would do: 
require.Nil(t, err, "Error creating aetest instance, got %v", err)

Besides asserting something to be nil, you can also assert.NotNil, or you can assert that two things are equal with assert.Equal:
assert.Equal(t, expectedResponse, string(rec.Body.Bytes()),
    "Expected response to be %s, got %s",
    expectedResponse, string(rec.Body.Bytes()))

There are tons more assertions besides those. You can assert that two things aren't equal with NotEqual, you can assert that a bool is true or false with True and False, you can assert that something is its type's zero value with Zero, and you can assert that a slice or map has a specific length with Len.

Pretty much, all your assertions in Assert and Require follow the format of assert.Something, then your testing.T, then the parameters of your assertion, and finally, an error message in printf format. So this cuts down on a lot of the bulk in our tests.

To see this in action, go get github.com/stretchr/testify/assert and go get github.com/stretchr/testify/require. When we replace our if statement assertions that an error is nil with assert.Nil and require.Nil and we replace our if statement assertions that two values are equal with assert.Equal, we get:
func TestAddLocationTestify(t *testing.T) {
    inst, err := aetest.NewInstance(
        &aetest.Options{StronglyConsistentDatastore: true})
    require.Nil(t, err, "Error creating aetest instance: %v", err)
    defer inst.Close()
    rt := initRouter()

    loc, err := json.Marshal(&Location{
        Name: "Cambridge Fresh Pond",
        Lat:  42.385658,
        Lng:  -71.149308,
    })
    assert.Nil(t, err, "Error marshalling Location into JSON: %v", err)

    req, err := inst.NewRequest("POST", "/add-location",
        ioutil.NopCloser(bytes.NewBuffer(loc)))
    require.Nil(t, err, "Error preparing request: %v", err)
    rec := httptest.NewRecorder()
    rt.ServeHTTP(rec, req)

    const expectedResponse = `{"addLocation":"success"}`
    assert.Equal(t, expectedResponse, string(rec.Body.Bytes()),
        "Expected response to be %s, got %s", expectedResponse, string(rec.Body.Bytes()))

    dbReq, err := inst.NewRequest("GET", "/", nil)
    require.Nil(t, err, "Error preparing request: %v", err)
    ctx := appengine.NewContext(dbReq)

    q := datastore.NewQuery("Location")
    numLocs, err := q.Count(ctx)
    require.Nil(t, err, "Error preparing request: %v", err)
    assert.Equal(t, 1, numLocs, "Expected number of locations to be 1, got %d", numLocs)
}
A test that's 14 lines shorter (46 vs 32) and much easier to read.

You know what else is cool? If a Testify assertion fails, it gives you a stack trace of what went wrong! Let's say we wanted a helper function to make our assert.Equals even shorter by having the expected and actual value parameters be typed only once:
func assertExpectedGot(t *testing.T, expected, got interface{}, msg string) {
    assert.Equal(t, expected, got, msg, expected, got)
}

Then if we tested this failing assertion 
assertExpectedGot(t, 0, numLocs, "Expected number of locations to be %d, got %d")

We would get an error trace that goes something like:
--- FAIL: TestAddLocationExpectedGot (4.89s)
     Error Trace:    api_test.go:101
                     api_test.go:135
     Error:          Not equal: 0 (expected)
                             != 1 (actual)
     Messages:       Expected number of locations to be 0, got 1

So you can use test helper functions anywhere you want with Testify Assert!


If you thought that was cool, wait until you get a load of Testify Suite!


Get rid of boilerplate with Testify Suite

So we have assertion syntax with Testify Assert and Require, but one thing that's kinda ugly about our test is this section of code at the beginning of the test:
inst, err := aetest.NewInstance(
    &aetest.Options{StronglyConsistentDatastore: true})
require.Nil(t, err, "Error creating aetest instance: %v", err)
defer inst.Close()
rt := initRouter()

If we had a lot of tests that use our router and an aetest instance, we would need this boilerplate section at the beginning of every test.

Aside from aesthetics, there's another issue with that. Starting an aetest instance means starting an instance of dev_appserver.py, which is really slow (takes about 2 or 3 seconds on my machine). This is what got me to start using Testify Suite at my job at Meta. I kept piling on more tests with aetest instances and soon enough, 4 minutes worth of tests had me waiting like

But I didn't need all those instances; all I needed was one aetest instance that started each test with a clean datastore. Luckily, I could get one instance I could use for all my tests with Testify Suite. My set of tests sped up to under a minute, running like


I'd say that's a good enough reason to go get github.com/stretchr/testify/suite.

Now let's make our suite. The things in the boilerplate we are creating and can reuse in other tests are the aetest instance and our router, so this is the struct we want:
type TestSuite struct {
    suite.Suite
    inst aetest.Instance
    rt   *mux.Router
}

The first line, suite.Suite, means we are making a struct that composes with Testify Suite's Suite type, which contains all the test suite functionality. Then, since we are making our own struct, we can give the struct whatever other struct fields and methods we want. Go composition in action!

To prepare the aetest instance and router, we give our TestSuite a SetupSuite method
func (s *TestSuite) SetupSuite() {
    var err error
    s.inst, err = aetest.NewInstance(
        &aetest.Options{StronglyConsistentDatastore: true})
    require.Nil(s.T(), err, "Error creating aetest instance: %v", err)
    s.rt = initRouter()
}
This sets up our instance and router. If a Testify suite has a SetupSuite method, that method is called before the suite starts running its tests, similar to before in Mocha.js.

Something else worth noting in this method is the call to s.T() in our require. A Testify suite comes with a testing.T, and you can gain access to it with the suite's T() method and use it just like you would in regular Go tests.

Similar to SetupSuite, you can also give a Testify suite a TearDownSuite method, which runs at the end of the test suite, similar to after in Mocha.
func (s *TestSuite) TearDownSuite() {
    err := s.inst.Close()
    assert.Nil(s.T(), err, "Error closing aetest instance: %v", err)
}
For code you want to run before or after each individual test, there's also SetupTest and TearDownTest, which work like beforeEach and afterEach, so if I wanted to clear the datastore at the end of each test, I would do that in the TearDownTest method.


Writing and running our test in Testify Suite

Now that we've gotten rid of the boilerplate, we only need to make a few modifications on TestAddLocation to change our regular Go test into a Testify Suite test. The first one is in the function signature:
func (s *TestSuite) TestAddLocation() {


Much like how go test runs all the tests whose names start with Test and take in a testing.T, a Testify suite runs all of its methods whose names start with Test and take in no arguments. Why no arguments? Because the suite already has its own testing.T! And to get that testing.T, we start the test with:
t := s.T()


The only things left to do are replace inst and rt with s.inst and s.rt and just like that, we have our Testify test good to go:
func (s *TestSuite) TestAddLocation() {
    t := s.T()

    loc, err := json.Marshal(&Location{
        Name: "Cambridge Fresh Pond",
        Lat:  42.385658,
        Lng:  -71.149308,
    })
    assert.Nil(t, err, "Error marshalling Location into JSON: %v", err)

    req, err := s.inst.NewRequest("POST", "/add-location",
        ioutil.NopCloser(bytes.NewBuffer(loc)))
    require.Nil(t, err, "Error preparing request: %v", err)
    rec := httptest.NewRecorder()
    s.rt.ServeHTTP(rec, req)

    const expectedResponse = `{"addLocation":"success"}`
    assert.Equal(t, expectedResponse, string(rec.Body.Bytes()),
        "Expected response to be %s, got %s", expectedResponse, string(rec.Body.Bytes()))

    dbReq, err := s.inst.NewRequest("GET", "/", nil)
    require.Nil(t, err, "Error preparing request: %v", err)
    ctx := appengine.NewContext(dbReq)

    q := datastore.NewQuery("Location")
    numLocs, err := q.Count(ctx)
    require.Nil(t, err, "Error preparing request: %v", err)
    assert.Equal(t, 1, numLocs, "Expected number of locations to be 1, got %d", numLocs)
}
Looks almost exactly like the version we had when we added Testify Assert and Require to our regular Go test, and that's the beauty of Testify Suite - it adds that test suite functionality to your tests but in a way that still feels like you're writing regular Go tests. In fact, the code to actually run your Testify suite is itself a regular Go test!
func TestRunSuite(t *testing.T) {
    suite.Run(t, new(TestSuite))
}

suite.Run takes in a testing.T and a Testify suite and runs all of the suite's tests. So as you can see, with Testify Assert, Require, and Suite, we get the extra testing convenience we would get in a Node library, but it still feels like writing regular Go code.

This Gopher gives these libraries two gopher front teeth up, as in, a gopher doing a handstand so its front teeth point upwards!

You can find the documentation for Testify Assert, Require, and Suite here, here, and here, and you can find the example tests from this tutorial here. Happy testing, and stay slothful!

Wednesday, February 4, 2015

Go/Gorilla for MEAN Stack developers part 2: Handlers and middleware

In my last tutorial we learned about HTTP routing using Go's net/http package and Gorilla's mux package, and we briefly talked about HTTP Handlers, which are actually a major building block of Go servers.

You can use Handlers to organize your web app's functionality with middleware like in Express. If you've worked with Express, you know middleware can carry out really useful functionality like authentication, logging, and input validation. So in this tutorial, we'll learn about what Handlers are and how to use them as middleware.

If you'd like to follow along, in your GOPATH's src directory add a gorilla-tutorial-2 directory with a file names server.go. If you want to follow along in Express as well, run npm install express and npm install morgan add a file named server.js.

You can find the source code for the Go and Express examples on this GitHub repository.

So what is a Handler?

The definition of a Handler in Go's http source code is:
type Handler interface{
    ServeHTTP(ResponseWriter, *Request)
}

So a Handler can be any type as long as it has a ServeHTTP method that takes in a ResponseWriter and a Request pointer. Hmm, those parameters sound familiar...
func serveHelloWorld(w http.ResponseWriter, r *http.Request){
    fmt.Fprintf(w, “Hello world!”)
}

As you can see, request-handling functions like serveHelloWorld can be used as ServeHTTP methods. Let's try that! Put this in server.go:
package main

import (
    "fmt"
    "net/http"
)

type helloWorldHandler struct{}                                              //1
func (h helloWorldHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){//2
    fmt.Fprintf(w, "Hello world!")
}

func main(){
    http.Handle("/", helloWorldHandler{})                                    //3
    http.ListenAndServe(":1123", nil)
}
1. Our helloWorldHandler type is just a blank struct with a ServeHTTP method.

2. We give our Handler a ServeHTTP method based on serveHelloWorld to satisfy the Handler interface

3. To put a Handler on a route in the server, instead of using http.HandleFunc, we use http.Handle.


Now if you request localhost:1123, your server should serve the message “Hello world!”. This server works like the hello world server in the last tutorial, except instead of using HandleFunc it uses Handler. Here's a diagram of how our new hello world server works.



You know what else looks like a ServeHTTP method taking in a ResponseWriter and a Request pointer? Express middleware taking in request and response objects!

function serveHelloWorld(req, res){
  res.send('Hello world!')
}

ServeHTTP methods of Handlers are the central to Go web middleware, and they work a lot like Express middleware. But the struct part of our helloWorldHandler was pointless. We only needed the ServeHTTP method. Luckily, we have a data type for making Handlers from functions.

Introducing HandlerFuncs

Go's http package defines this awesome data type

type HandlerFunc func(ResponseWriter, *Request)

that converts any function taking in a ResponseWriter and Request pointer to a Handler.


To try that out, add the serveHelloWorld function in server.go and replace the first line of main with
helloHandler := http.HandlerFunc(serveHelloWorld)
http.Handle("/", helloHandler)

And your server should still work the same, and just like on an Express server, you're using a function to handle your routes. As in, a function that happens to be a Handler. What's really cool is what ServeHTTP method HandlerFuncs use to satisfy the Handler interface:
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request){
    f(w, r)
}

A HandlerFunc's ServeHTTP method is a function that calls the HandlerFunc itself! A HandlerFunc is basically ITS OWN SERVEHTTP METHOD!



I know, right? This diagram shows what's going on with our HandlerFunc.


By the way, this is how http.HandleFunc works. Behind the scenes it takes the function you give it and converts it to a HandlerFunc.

So we know a Handler is any type with a ServeHTTP method. Now let's see how those ServeHTTPs can be used for middleware chaining.

ServeHTTP() is the next() of Go middleware chaining

In Express, what's really great about middlewares is you can add multiple middlewares to the same route and have them run in a chain so each middleware handles a different functionality.

In Express you make a middleware function part of a chain by giving it a next parameter after its req and res parameters. That next parameter is a function used to call the next middleware. For an example, if you're following along in Express, add this to server.js:

var express = require('express'),
    timers  = require('timers');

var app = express();

var sleepMiddleware = function(req, res, next){         //1
  console.log('Sloth is sleeping, please wait');
  timers.setTimeout(function(){
    next();                                             //1
  }, 3000);
}

app.use('/sloths', sleepMiddleware, function(req, res){ //2
  res.send('<img src="http://andyhaskell.github.io/Slothful-Soda/images/sloth.jpg" width="240px" height="300px" />');
});

app.use('/', function(req, res){
  res.send('Hello world!');
});

app.listen(1123);

1. sleepMiddleware takes in a request, a response, and the next middleware. It tells us the sloth is sleeping, pauses for 3 seconds, and then calls the next middleware by calling next() after the pause.

2. On “/sloths” we call sleepMiddleware followed by an anonymous middleware serving a picture of a sloth. That's our middleware chain, and the anonymous middleware is sleepMiddleware's next().


If you run node server.js and go to localhost:1123/sloths, you should get:



In Express, we make one middleware call another by calling next() in the first function.
In Go, we have one middleware call the next one by calling the next Handler's ServeHTTP in the first Handler's ServeHTTP!

To try this, in server.go add "time" to the list of imported packages and add this function:
func sloth(w http.ResponseWriter, r *http.Request){
    fmt.Fprintf(w, `<body><img src="http://andyhaskell.github.io/Slothful-Soda/images/sloth.jpg" width="240px" height="300px" /></body>`)
}

Then in the beginning of main add this code:
slothHandler := http.HandlerFunc(sloth)             //1

sleepHandler := http.HandlerFunc(
      func(w http.ResponseWriter, r *http.Request){ //2
    fmt.Println("Sloth is sleeping, please wait")
    time.Sleep(3000 * time.Millisecond)
    slothHandler.ServeHTTP(w, r)                    //2
})

http.Handle("/sloths", sleepHandler)                //3

1. In slothHandler, we serve the sloth picture.

2. sleepHandler tells the user the sloth is sleeping and pauses 3 seconds. Then slothHandler.ServeHTTP is called, causing slothHandler to take over, serving the sloth picture.

3. sleepHandler calls slothHandler so we have a chain. Since sleepHandler starts the chain, we put the chain on the “/sloths” route by putting sleepHandler on the route.


If you run go install and gorilla-tutorial-2 and then go to localhost:1123/sloths you should get the same results as in the Express version.

Here's a diagram of how that works:

 
Chaining with closures

In the last example, we had a chain of middlewares, but the order they ran in was hard-coded. It'd be good if our chain worked like Express where we define the middleware to call next() without knowing what that next middleware will be.

Luckily, Go supports closures, where we can pass in arguments to a function that then uses those arguments to make a new function.

So what if we had a function that took in a Handler and returned a HandlerFunc that does some sort of functionality and then called the Handler's ServeHTTP? Then we could pass in any Handler we wanted and we'd get back a HandlerFunc chained with our Handler!

To see this in action, add this function to server.go:
func sleepConstructor(h http.Handler) http.Handler{                       //1
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){ //2
        fmt.Println("Sloth is sleeping, please wait")
        time.Sleep(3000 * time.Millisecond)
        h.ServeHTTP(w, r)                                                 //3
    })
}

And add this code to main:
http.Handle("/sloths2", sleepConstructor(slothHandler))                   //4

Here's what's going on:

1. sleepConstructor takes in a Handler and returns a Handler. That's the key feature of this trick.

2. We have our sleepHandler function as the HandlerFunc to be returned, except...

3. Instead of calling the ServeHTTP of a pre-defined Handler, it calls the ServeHTTP of the Handler passed in, chaining sleepConstructor's returned HandlerFunc with the Handler passed in.

4. sleepConstructor(slothHandler) makes our chain, which we pass to "/sloths2".

 
So we can make a middleware chain by making a function that takes in one Handler and gives us a middleware function chained to that Handler, that function itself being a Handler.

That's actually how StripPrefix/FileServer works

Here's a practical example of Go middleware chaining. Remember how in the last tutorial we had a StripPrefix function? And how we gave that function our FileServer?
http.Handle("/images/",
              http.StripPrefix("/images/",
                http.FileServer(http.Dir("public/images"))))

FileServer gives us a Handler, which we pass into StripPrefix. What we get back from StripPrefix is itself a Handler, so this is starting to look like another middleware constructor.

If you look in the code for StripPrefix, you'll see that's exactly what StripPrefix is. When we call StripPrefix, we get back a HandlerFunc that removes a prefix from the Request's path and then calls our next Handler's ServeHTTP, which, in the case of our image server, serves our images.

Here's what that looks like:


By the way, we can make our chains as long as we want and in any order

Since passing a Handler into a middleware constructor gives us our chain as a new Handler, we could in turn pass the chain into another middleware constructor and get back yet another Handler with its functionality added to the chain. So we can chain as many middlewares as we want.

Sloths love hibiscus flowers, so in server.go let's make a teaConstructor function that creates a HandlerFunc that has our sloth drink a cup of hibiscus tea before calling the next ServeHTTP:
func teaConstructor(h http.Handler) http.Handler{
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
        fmt.Println("*drinks hibiscus tea*")
        time.Sleep(500 * time.Millisecond)
        h.ServeHTTP(w, r)
    })
}

Now we can chain teaConstructor and sleepConstructor in any order we want. To try that, add these chains to main in server.go:
// In this chain the sloth sleeps, drinks some hibiscus tea, and then
// serves our sloth picture.
//
//                 sleepConstructor takes in the Handler constructed from
//                         calling teaConstructor(slothHandler)
//                                        |
//                                        V
sleepTeaSlothChain := sleepConstructor(teaConstructor(slothHandler))


// In this chain the sloth drinks some hibiscus tea, sleeps, and then
// serves our sloth picture. Note that since teaConstructor and
// sleepConstructor take in a Handler and return a Handler, we can
// have the constructors call each other in different orders to run the
// middlewares in a different order.
teaSleepSlothChain := teaConstructor(sleepConstructor(slothHandler))


// In this chain the sloth drinks two cups of hibiscus tea and then serves
// our sloth picture. Since middleware constructors take in and return a
// Handler, we can call the same constructor more than once in a chain to
// run a middleware twice.
teaTwiceChain      := teaConstructor(teaConstructor(slothHandler))

As you can see, if you have some middleware constructors, you can put them in any order. But if you have a lot of constructors, the chain can quickly get annoying to read and build upon.

Luckily, there are several Go libraries, like MuxChain, Negroni, and Alice, that you can use for more readable middleware chaining. In the next section, I'll show you how the Alice library not only makes middleware easier to work with, it also makes it real familiar if you are coming from an Express background.

Express-like middleware chaining with Alice

Alice is a very simple middleware chaining library to work with. It's so simple that at the time I'm writing this, there were just 36 lines of code in the library excluding whitespace and comments. To get it, just run go get github.com/justinas/alice.

Then in server.go, import "github.com/justinas/alice".

Now let's see what the middleware chains from before would look like if we used Alice:
sleepTeaSlothChain :=
  alice.New(sleepConstructor, teaConstructor).Then(slothHandler)
     //1                  //2                       //3

teaSleepSlothChain :=
  alice.New(teaConstructor, sleepConstructor).Then(slothHandler)

teaTwiceChain :=
  alice.New(teaConstructor, teaConstructor).Then(slothHandler)

1. We make a middleware chain with alice.New.

2. We pass our middleware constructor functions (which can be any function that takes in a Handler and returns a Handler) into alice.New in the order we want their middlewares called in. We can pass in as many constructors as we want to add to the chain.

3. We need to pass in a Handler to complete the chain, so then we pass slothHandler into Then, which causes Alice to construct a middleware chain from the constructors and Handler.


Let's try passing sleepTeaSlothChain into our Go router using Alice. Add this to main in server.go.
http.Handle("/sloths3",
            alice.New(sleepConstructor, teaConstructor).Then(slothHandler))

Compare that to what it would look like in Express if we had those middlewares:
app.use('/sloths3',   sleepMiddleware,  teaMiddleware,       serveSloth)

What if we wanted to apply a middleware to every route on the server?

Sometimes you might want a middleware to be run on every request your server gets. In Express, you would do that with this syntax:

app.use('/', yourMiddleware)

Or just app.use(yourMiddleware) since / is the default route. One example of this would be running a request logger middleware that gives you information about all the requests your server gets.


In server.js, add this line to the list of modules you're requiring:

var logger = require('morgan');

And right after the line var app = express(); add the line

app.use(logger('common'));

Now when a request goes to your Express server, the first route it will reach is your logger route that all routes match, so your request will be logged to the console no matter what route your request is to.


In Go, we have a catch-all route, so we could put a logging middleware on the catch-all route, but how would that request then get to all the other routes on the server? It turns out, every Go server has one Handler all of its requests go to. And where can we add that Handler to the server?

http.ListenAndServe(":1123", nil)

The first parameter of ListenAndServe, as we know, is the port we are listening on. That second parameter that we are passing nil into is a Handler. So if we passed in a HandlerFunc for serveHelloWorld, all requests would be handled with serveHelloWorld, getting the response “Hello world!”.


But if we don't pass in any Handler to ListenAndServe, a special router Handler called http.DefaultServeMux is used instead.

That DefaultServeMux is where all your routes go when you use http.Handle and http.HandleFunc. When it gets a request, DefaultServeMux matches the request's path with a route and then calls ServeHTTP for that route's Handler.

In other words, DefaultServeMux is a middleware that chains with the Handlers on its routes.


And if DefaultServeMux is a middleware, it can be chained with other middlewares, like:

http.ListenAndServe(":1123", LoggingMiddlewareConstructor(http.DefaultServeMux))


Let's try that! In Gorilla, there is a package of useful middleware handlers called the handlers package so to get it, go get github.com/gorilla/handlers.

Add “github.com/gorilla/handlers” and “os” to the list of packages you are importing in server.go.

And then in server.go replace this line

http.ListenAndServe(":1123", nil)

with:
muxWithLog := handlers.LoggingHandler(os.Stdout, http.DefaultServeMux) //1
http.ListenAndServe(":1123", muxWithLog)                               //2

1. We pass os.Stdout and DefaultServeMux into the Gorilla LoggingHandler function to create a new middleware chain, muxWithLog. That chain starts with the request going to our LoggingHandler, which logs the request and then passes it to the DefaultServeMux to route and serve the request.

2. We then use it on our server by passing muxWithLog to ListenAndServe.

Now if we request localhost:1123/sloths3, we should get:


As you can see from this tutorial, although the building block of a Go net/http or Gorilla server is a Handler, these Handlers are a lot like Express.js middleware. Because of the more strict type system in Go, it is somewhat more complicated to use Handlers. But overall with both the net/http middleware chaining and Gorilla's routing Go + Gorilla has a lot of the flexibility of Express.

If you want to go deeper into learning about middleware in Go, I highly recommend trying out Negroni and MuxChain as alternatives to Alice and also looking into the other middleware in Gorilla's handlers package. Until next time, stay slothful!


Image credits:
  • The author of the picture of the sloth in the examples is Stefan Laube and the picture is public domain.
  • The author of the picture of the sloth at the end of the tutorial is Emma Websdale and the picture is licensed under CC BY 2.0.

Also, shoutout to my parents and Jai Singh from Boston Golang for proofreading this tutorial and to my dog Lola for making a perfect reaction face for finding out how HandlerFunc works.