Middleware Example
Advanced middleware patterns including authentication, rate limiting, and custom middleware
Complete Middleware Example
This example demonstrates various middleware patterns:
package main
import (
"context"
"fmt"
"log"
"strings"
"sync"
"time"
"github.com/user001/squirrel"
)
// User represents an authenticated user
type User struct {
ID string `json:"id"`
Username string `json:"username"`
Role string `json:"role"`
}
// Rate limiter
type RateLimiter struct {
requests map[string][]time.Time
mutex sync.Mutex
limit int
window time.Duration
}
func NewRateLimiter(limit int, window time.Duration) *RateLimiter {
return &RateLimiter{
requests: make(map[string][]time.Time),
limit: limit,
window: window,
}
}
func (rl *RateLimiter) Allow(clientIP string) bool {
rl.mutex.Lock()
defer rl.mutex.Unlock()
now := time.Now()
// Clean old requests
if requests, exists := rl.requests[clientIP]; exists {
var validRequests []time.Time
for _, reqTime := range requests {
if now.Sub(reqTime) < rl.window {
validRequests = append(validRequests, reqTime)
}
}
rl.requests[clientIP] = validRequests
}
// Check if limit exceeded
if len(rl.requests[clientIP]) >= rl.limit {
return false
}
// Add current request
rl.requests[clientIP] = append(rl.requests[clientIP], now)
return true
}
// Global rate limiter (100 requests per minute)
var rateLimiter = NewRateLimiter(100, time.Minute)
// Mock user database
var users = map[string]*User{
"token123": {ID: "1", Username: "john", Role: "user"},
"token456": {ID: "2", Username: "admin", Role: "admin"},
}
func main() {
mux := squirrel.NewSqurlMux()
// Global middleware (applied to all routes)
mux.Use(loggingMiddleware())
mux.Use(recoveryMiddleware())
mux.Use(corsMiddleware())
mux.Use(rateLimitMiddleware())
// Public routes
mux.HandleFunc("GET /", homeHandler)
mux.Use("GET /public", publicHandler)
// Protected routes (require authentication)
protectedMux := squirrel.NewSqurlMux()
protectedMux.Use(authMiddleware())
protectedMux.HandleFunc("GET /profile", profileHandler)
protectedMux.HandleFunc("GET /dashboard", dashboardHandler)
// Admin routes (require admin role)
adminMux := squirrel.NewSqurlMux()
adminMux.Use(authMiddleware())
adminMux.Use(adminMiddleware())
adminMux.HandleFunc("GET /admin/users", adminUsersHandler)
adminMux.HandleFunc("DELETE /admin/users/{id}", adminDeleteUserHandler)
// Mount sub-routers
mux.Handle("/api/", squirrel.StripPrefix("/api", protectedMux))
mux.Handle("/api/admin/", squirrel.StripPrefix("/api", adminMux))
fmt.Println("Server with middleware starting on http://localhost:8080")
log.Fatal(squirrel.ListenAndServe(":8080", mux))
}
// Logging middleware
func loggingMiddleware() squirrel.Middleware {
return func(next squirrel.HandlerFunc) squirrel.HandlerFunc {
return func(w *squirrel.Response, r *squirrel.Request) {
start := time.Now()
// Add request ID
requestID := fmt.Sprintf("%d", time.Now().UnixNano())
w.Header().Set("X-Request-ID", requestID)
// Call next handler
next(w, r)
// Log request details
duration := time.Since(start)
log.Printf("[%s] %s %s - %v", requestID, r.Method, r.URL.Path, duration)
}
}
}
// Recovery middleware
func recoveryMiddleware() squirrel.Middleware {
return func(next squirrel.HandlerFunc) squirrel.HandlerFunc {
return func(w *squirrel.Response, r *squirrel.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("Panic recovered: %v", err)
w.WriteStatus(500)
w.JSON(map[string]string{
"error": "Internal server error",
})
}
}()
next(w, r)
}
}
}
// CORS middleware
func corsMiddleware() squirrel.Middleware {
return func(next squirrel.HandlerFunc) squirrel.HandlerFunc {
return func(w *squirrel.Response, r *squirrel.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
if r.Method == "OPTIONS" {
w.WriteStatus(200)
return
}
next(w, r)
}
}
}
// Rate limiting middleware
func rateLimitMiddleware() squirrel.Middleware {
return func(next squirrel.HandlerFunc) squirrel.HandlerFunc {
return func(w *squirrel.Response, r *squirrel.Request) {
clientIP := getClientIP(r)
if !rateLimiter.Allow(clientIP) {
w.WriteStatus(429)
w.JSON(map[string]string{
"error": "Rate limit exceeded",
"retry_after": "60",
})
return
}
next(w, r)
}
}
}
// Authentication middleware
func authMiddleware() squirrel.Middleware {
return func(next squirrel.HandlerFunc) squirrel.HandlerFunc {
return func(w *squirrel.Response, r *squirrel.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
w.WriteStatus(401)
w.JSON(map[string]string{
"error": "Authorization header required",
})
return
}
// Extract token (Bearer token format)
parts := strings.Split(authHeader, " ")
if len(parts) != 2 || parts[0] != "Bearer" {
w.WriteStatus(401)
w.JSON(map[string]string{
"error": "Invalid authorization format",
})
return
}
token := parts[1]
user, exists := users[token]
if !exists {
w.WriteStatus(401)
w.JSON(map[string]string{
"error": "Invalid token",
})
return
}
// Add user to request context
ctx := context.WithValue(r.Context(), "user", user)
r = r.WithContext(ctx)
next(w, r)
}
}
}
// Admin middleware (requires admin role)
func adminMiddleware() squirrel.Middleware {
return func(next squirrel.HandlerFunc) squirrel.HandlerFunc {
return func(w *squirrel.Response, r *squirrel.Request) {
user, ok := r.Context().Value("user").(*User)
if !ok {
w.WriteStatus(500)
w.JSON(map[string]string{
"error": "User context not found",
})
return
}
if user.Role != "admin" {
w.WriteStatus(403)
w.JSON(map[string]string{
"error": "Admin access required",
})
return
}
next(w, r)
}
}
}
// Timing middleware
func timingMiddleware() squirrel.Middleware {
return func(next squirrel.HandlerFunc) squirrel.HandlerFunc {
return func(w *squirrel.Response, r *squirrel.Request) {
start := time.Now()
next(w, r)
duration := time.Since(start)
w.Header().Set("X-Response-Time", duration.String())
}
}
}
// Helper function to get client IP
func getClientIP(r *squirrel.Request) string {
// Check X-Forwarded-For header
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
return strings.Split(xff, ",")[0]
}
// Check X-Real-IP header
if xri := r.Header.Get("X-Real-IP"); xri != "" {
return xri
}
// Fall back to RemoteAddr
return r.RemoteAddr
}
// Handler functions
func homeHandler(w *squirrel.Response, r *squirrel.Request) {
w.JSON(map[string]string{
"message": "Welcome to the middleware example",
"status": "public",
})
}
func publicHandler(w *squirrel.Response, r *squirrel.Request) {
w.JSON(map[string]string{
"message": "This is a public endpoint",
"time": time.Now().Format(time.RFC3339),
})
}
func profileHandler(w *squirrel.Response, r *squirrel.Request) {
user := r.Context().Value("user").(*User)
w.JSON(map[string]interface{}{
"message": "User profile",
"user": user,
})
}
func dashboardHandler(w *squirrel.Response, r *squirrel.Request) {
user := r.Context().Value("user").(*User)
w.JSON(map[string]interface{}{
"message": "User dashboard",
"user_id": user.ID,
"data": []string{"item1", "item2", "item3"},
})
}
func adminUsersHandler(w *squirrel.Response, r *squirrel.Request) {
var userList []*User
for _, user := range users {
userList = append(userList, user)
}
w.JSON(map[string]interface{}{
"message": "Admin users list",
"users": userList,
})
}
func adminDeleteUserHandler(w *squirrel.Response, r *squirrel.Request) {
userID := r.PathValue("id")
w.JSON(map[string]interface{}{
"message": "User deleted (simulated)",
"user_id": userID,
})
}Testing the Middleware
Public Endpoints
curl http://localhost:8080/curl http://localhost:8080/publicProtected Endpoints (Require Authentication)
curl -H "e;Authorization: Bearer token123"e; http://localhost:8080/api/profilecurl -H "e;Authorization: Bearer token123"e; http://localhost:8080/api/dashboardAdmin Endpoints (Require Admin Role)
curl -H "e;Authorization: Bearer token456"e; http://localhost:8080/api/admin/userscurl -X DELETE -H "e;Authorization: Bearer token456"e; http://localhost:8080/api/admin/users/1Rate Limiting Test
for i in {1..105}; do curl http://localhost:8080/; doneAfter 100 requests, you will get a 429 rate limit error
Middleware Features
Security Middleware
- • Authentication with Bearer tokens
- • Role-based authorization
- • CORS handling
- • Rate limiting
Utility Middleware
- • Request logging
- • Panic recovery
- • Response timing
- • Request ID generation
Advanced Patterns
- • Middleware chaining
- • Conditional middleware
- • Context passing
- • Sub-router mounting
Error Handling
- • Structured error responses
- • HTTP status codes
- • Graceful degradation
- • Request validation
Middleware Order
📋 Important: Middleware Execution Order
- Recovery (catch panics first)
- Logging (log all requests)
- CORS (handle preflight requests)
- Rate limiting (before expensive operations)
- Authentication (verify user identity)
- Authorization (check permissions)
- Business logic (your handlers)
Production Enhancements
- Use Redis for distributed rate limiting
- Implement JWT token validation
- Add request/response compression
- Implement circuit breaker patterns
- Add metrics and monitoring middleware
- Use structured logging (JSON format)
- Implement request timeout middleware
Next Steps
- Explore the file upload example
- Learn about advanced error handling
- Read the middleware guide
- Check out middleware API reference