Go
Beginner
1 min read
Middleware and Handler Chains
Example
package main
import (
"fmt"
"log"
"net/http"
"time"
)
// responseRecorder wraps ResponseWriter to capture the status code.
type responseRecorder struct {
http.ResponseWriter
status int
}
func (rr *responseRecorder) WriteHeader(code int) {
rr.status = code
rr.ResponseWriter.WriteHeader(code)
}
// loggingMiddleware logs method, path, status, and duration.
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rr := &responseRecorder{ResponseWriter: w, status: http.StatusOK}
next.ServeHTTP(rr, r)
log.Printf("%s %s %d %v", r.Method, r.URL.Path, rr.status, time.Since(start))
})
}
// authMiddleware rejects requests without an API key header.
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-API-Key") != "secret" {
http.Error(w, "unauthorised", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
func chain(h http.Handler, mws ...func(http.Handler) http.Handler) http.Handler {
for i := len(mws) - 1; i >= 0; i-- {
h = mws[i](h)
}
return h
}
func main() {
hello := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, authenticated user!")
})
mux := http.NewServeMux()
mux.Handle("/secure", chain(hello, loggingMiddleware, authMiddleware))
mux.Handle("/public", loggingMiddleware(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, public!")
}),
))
log.Println("listening on :8080")
log.Fatal(http.ListenAndServe(":8080", mux))
}