Creating a basic middleware
Building middleware functions is simple and straightforward. Let's build a program based on the knowledge gained from Chapter 2, Handling Routing for our REST Services. If you are not familiar with closure functions, a closure function returns another function. This principle helps us write middleware. A middleware should return another function, which can be either a middleware or a function handler. It is similar to JavaScript chain methods, whereby one function returns a new function as a return value. Let's create a closure function in Go, by doing the following:
- Create a program file, like so:
touch -p $GOPATH/src/github.com/git-user/chapter3/closureExample/main.go
We use this file to add our code.
- A closure function returns another function. Let's create a closure function that generates positive integers, using the following code:
// This function returns another function
func generator() func() int { // Outer function
var i = 0
return func() int { // Inner function
i++
return i
}
}
The function is a generator that returns a sequence of integers. A generator pattern generates a new item each time, based on given conditions. The inner function is returning an anonymous function with no arguments and one return type of integer. The i variable that is defined inside the outer function is available to the anonymous function, making it remember the state between upcoming function calls.
- Now, we can use the previous generator in our main program, like this:
package main
import (
"fmt"
)
...
func main() {
numGenerator := generator()
for i := 0; i < 5; i++ {
fmt.Print(numGenerator(), "\t")
}
}
- We can run the previous code as a standalone program, as follows:
go run $GOPATH/src/github.com/git-user/chapter3/closureExample/main.go
The following numbers will be generated and printed using Tab spaces:
1 2 3 4 5
In Go, the function signature of the outer function should exactly match the anonymous function's signature. In the previous example, func() int is the signature for both the outer and inner functions. The only exception is that the outer function can have an interface as a return type, and the inner function can implement that interface. We will see how in the next few lines.
Now, coming to how closures help to build a middleware: any generator function that can return another function that satisfies the http.Handler interface can be a middleware. Let's validate this statement with an example, as follows:
- Create a file for our program, like this:
touch -p $GOPATH/src/github.com/git-user/chapter3/customMiddleware/main.go
- The middleware takes a normal HTTP handler function as its argument and returns another handler function. The function looks like this:
func middleware(originalHandler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter,
r *http.Request) {
fmt.Println("Executing middleware before request phase!")
// Pass control back to the handler
originalHandler.ServeHTTP(w, r)
fmt.Println("Executing middleware after response phase!")
})
}
If you notice the preceding middleware function, it is taking originalHandler, an HTTP handler, as its argument, and is returning another HTTP handler. The inner function is using the original handler to execute the logic. Before and after that handler is where the middleware operates on request and response objects. This makes all the requests coming to the main handler pass through the middleware logic.
- Now, Let's define the main logic that uses the middleware function we have created, as follows:
package main
import (
"fmt"
"net/http"
)
func handle(w http.ResponseWriter, r *http.Request) {
// Business logic goes here
fmt.Println("Executing mainHandler...")
w.Write([]byte("OK"))
}
func main() {
// HandlerFunc returns a HTTP Handler
originalHandler := http.HandlerFunc(handle)
http.Handle("/", middleware(originalHandler))
http.ListenAndServe(":8000", nil)
}
- Run the code, as follows:
go run $GOPATH/src/github.com/git-user/chapter3/customMiddleware/main.go
- If you do a curl request to—or visit— http://localhost:8000 in your browser, the console will receive this message:
Executing middleware before request phase!
Executing mainHandler...
Executing middleware after response phase!
This program is denoted by the rectangle block to the right in the preceding diagram, with the label CUSTOM MIDDLEWARE. If you observe the middleware visual illustration provided previously, the request phase direction is to the right, and the response direction is to the left.
Go web frameworks such as Martini and Gin provide middleware by default. We will see more about them in Chapter 4, Simplifying RESTful Services with Popular Go Frameworks. It is good for a developer to understand the low-level details of middleware.
The following diagram can help you understand how the logic flow happens in the middleware. This diagram explains how a handler is converted into a wrapper handler:
We have seen the creation of a simple middleware, but in a real scenario, multiple middleware are required to log requests, authenticate, and so on. In the next section, we will see how to chain multiple middleware.