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.

1 comment :

  1. Hi

    Nice Article. But instead of using alice I would use: https://github.com/codegangsta/negroni

    BR, Rene

    ReplyDelete