Loading...
Loading...

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
    })
}
0 Interaction
0 Views
Views
0 Likes
×
×
×
🍪 CookieConsent@Ptutorials:~

Welcome to Ptutorials

$ Allow cookies on this site ? (y/n)

top-home