The following steps cover the writing and running of your application:
- From your Terminal or console application, create a new directory called ~/projects/go-programming-cookbook/chapter8/middleware, and navigate to this directory.
- Run the following command:
$ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter8/middleware
You should see a file called go.mod that contains the following:
module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter8/middleware
- Copy the tests from ~/projects/go-programming-cookbook-original/chapter8/middleware, or use this as an exercise to write some of your own code!
- Create a file called middleware.go with the following contents:
package middleware
import (
"log"
"net/http"
"time"
)
// Middleware is what all middleware functions will return
type Middleware func(http.HandlerFunc) http.HandlerFunc
// ApplyMiddleware will apply all middleware, the last
// arguments will be the
// outer wrap for context passing purposes
func ApplyMiddleware(h http.HandlerFunc, middleware
...Middleware) http.HandlerFunc {
applied := h
for _, m := range middleware {
applied = m(applied)
}
return applied
}
// Logger logs requests, this will use an id passed in via
// SetID()
func Logger(l *log.Logger) Middleware {
return func(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
l.Printf("started request to %s with id %s", r.URL,
GetID(r.Context()))
next(w, r)
l.Printf("completed request to %s with id %s in
%s", r.URL, GetID(r.Context()), time.Since(start))
}
}
}
- Create a file called context.go with the following contents:
package middleware
import (
"context"
"net/http"
"strconv"
)
// ContextID is our type to retrieve our context
// objects
type ContextID int
// ID is the only ID we've defined
const ID ContextID = 0
// SetID updates context with the id then
// increments it
func SetID(start int64) Middleware {
return func(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), ID,
strconv.FormatInt(start, 10))
start++
r = r.WithContext(ctx)
next(w, r)
}
}
}
// GetID grabs an ID from a context if set
// otherwise it returns an empty string
func GetID(ctx context.Context) string {
if val, ok := ctx.Value(ID).(string); ok {
return val
}
return ""
}
- Create a file called handler.go with the following contents:
package middleware
import (
"net/http"
)
// Handler is very basic
func Handler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("success"))
}
- Create a new directory named example and navigate to it.
- Create a file called main.go with the following contents:
package main
import (
"fmt"
"log"
"net/http"
"os"
"github.com/PacktPublishing/
Go-Programming-Cookbook-Second-Edition/
chapter8/middleware"
)
func main() {
// We apply from bottom up
h := middleware.ApplyMiddleware(
middleware.Handler,
middleware.Logger(log.New(os.Stdout, "", 0)),
middleware.SetID(100),
)
http.HandleFunc("/", h)
fmt.Println("Listening on port :3333")
err := http.ListenAndServe(":3333", nil)
panic(err)
}
- Run go run main.go.
- You could also run the following commands:
$ go build
$ ./example
You should see the following output:
$ go run main.go
Listening on port :3333
- In a separate Terminal, run the following curl command several times:
$ curl http://localhost:3333
You should see the following output:
$ curl http://localhost:3333
success
$ curl http://localhost:3333
success
$ curl http://localhost:3333
success
- In the original main.go, you should see the following:
Listening on port :3333
started request to / with id 100
completed request to / with id 100 in 52.284µs
started request to / with id 101
completed request to / with id 101 in 40.273µs
started request to / with id 102
- The go.mod file may be updated and the go.sum file should now be present in the top-level recipe directory.
- If you copied or wrote your own tests, go up one directory and run go test. Ensure that all tests pass.