Go Middleware Tutorial
Middleware in Go are functions that wrap HTTP handlers to provide cross-cutting concerns like logging, authentication, and request processing. This tutorial explains middleware patterns from basic to advanced.
1. Middleware Basics
1.1 What is Middleware?
Middleware are chained functions that process HTTP requests before/after the main handler. They:
- Intercept incoming requests
- Modify requests/responses
- Execute shared logic across routes
func basicMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Pre-processing
fmt.Println("Before handler")
next.ServeHTTP(w, r) // Call next handler
// Post-processing
fmt.Println("After handler")
})
}
2. Practical Middleware Examples
2.1 Logging Middleware
Tracks request details and response times:
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf(
"%s %s %v",
r.Method,
r.URL.Path,
time.Since(start),
)
})
}
2.2 Authentication Middleware
Validates requests before processing:
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if !isValidToken(token) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
3. Middleware Chaining
3.1 Basic Chaining
Multiple middleware can wrap each other:
func main() {
router := http.NewServeMux()
router.HandleFunc("/", handler)
// Apply middleware stack
wrapped := loggingMiddleware(authMiddleware(router))
http.ListenAndServe(":8080", wrapped)
}
3.2 Using Helper Libraries
Libraries like alice simplify chaining:
func main() {
chain := alice.New(
loggingMiddleware,
authMiddleware,
).Then(router)
http.ListenAndServe(":8080", chain)
}
4. Advanced Middleware Patterns
4.1 Context Propagation
Share data between middleware using context:
func userMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := getUserFromRequest(r)
ctx := context.WithValue(r.Context(), "user", user)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// In handler
user := r.Context().Value("user").(*User)
4.2 Error Handling
Centralized error processing:
func errorMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"error": "Internal server error",
})
}
}()
next.ServeHTTP(w, r)
})
}
5. Framework-Specific Middleware
5.1 Chi Router Middleware
Chi's middleware system with Use():
func main() {
r := chi.NewRouter()
// Global middleware
r.Use(
middleware.Logger,
middleware.Recoverer,
)
// Route-specific middleware
r.With(authMiddleware).Get("/admin", adminHandler)
}
5.2 Gin Middleware
Gin's middleware syntax:
func main() {
r := gin.Default()
// Global middleware
r.Use(gin.Logger(), gin.Recovery())
// Route group with middleware
admin := r.Group("/admin", authMiddleware)
{
admin.GET("/dashboard", dashboardHandler)
}
}
6. Best Practices
6.1 Middleware Design Principles
- Single responsibility: Each middleware should do one thing
- Reusability: Design middleware to be route-agnostic
- Performance: Avoid expensive operations in middleware
- Explicit ordering: Document middleware execution order
6.2 Common Pitfalls
// ❌ Bad: Forgetting to call next()
func badMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("This will break the chain!")
// Missing next.ServeHTTP()
})
}
// ✅ Good: Always call next unless intentionally terminating
func goodMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/blocked" {
http.Error(w, "Forbidden", http.StatusForbidden)
return // Explicit termination
}
next.ServeHTTP(w, r) // Normal continuation
})
}
×