Ejemplos de Características Web Web Go

Ejemplos de características específicas Web Go incluyendo manejo de rutas, middleware y servicio de archivos estáticos

💻 Servicio de Archivos Estáticos go

🟢 simple ⭐⭐⭐

Servir archivos estáticos, CSS, JavaScript, imágenes con almacenamiento en caché y encabezados apropiados

⏱️ 20 min 🏷️ go, web, static files
Prerequisites: Basic Go, net/http package, file io
// Web Go Static File Serving Examples
// Serve static files with caching and headers

package main

import (
	"encoding/base64"
	"fmt"
	"io"
	"log"
	"mime"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// 1. Basic Static File Server

// FileServer serves files from a directory
func FileServer(root http.FileSystem) http.Handler {
	fs := http.FileServer(root)

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Clean the path
		path := filepath.Clean(r.URL.Path)

		// Open file
		file, err := root.Open(path)
		if err != nil {
			if os.IsNotExist(err) {
				http.NotFound(w, r)
			} else {
				http.Error(w, "Internal Server Error", http.StatusInternalServerError)
			}
			return
		}
		defer file.Close()

		// Get file info
		stat, err := file.Stat()
		if err != nil {
			http.Error(w, "Internal Server Error", http.StatusInternalServerError)
			return
		}

		// Serve file
		http.ServeContent(w, r, path, stat.ModTime(), file)
	})
}

// 2. Static File Server with Custom Headers

// StaticServer serves static files with custom headers
type StaticServer struct {
	root    string
	headers map[string]string
}

// NewStaticServer creates a new static file server
func NewStaticServer(root string) *StaticServer {
	return &StaticServer{
		root:    root,
		headers: make(map[string]string),
	}
}

// AddHeader adds a default header
func (ss *StaticServer) AddHeader(key, value string) {
	ss.headers[key] = value
}

// ServeHTTP serves HTTP requests
func (ss *StaticServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Clean path
	path := filepath.Join(ss.root, filepath.Clean(r.URL.Path))

	// Get file info
	info, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			http.NotFound(w, r)
		} else {
			http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		}
		return
	}

	// Check if it's a directory
	if info.IsDir() {
		// Try to serve index.html
	 indexPath := filepath.Join(path, "index.html")
		if _, err := os.Stat(indexPath); err == nil {
			path = indexPath
			info, _ = os.Stat(path)
		} else {
			// Directory listing (for development)
			ss.serveDirectoryListing(w, r, path)
			return
		}
	}

	// Detect content type
	ext := filepath.Ext(path)
	contentType := mime.TypeByExtension(ext)
	if contentType == "" {
		contentType = "application/octet-stream"
	}

	// Set headers
	for key, value := range ss.headers {
		w.Header().Set(key, value)
	}
	w.Header().Set("Content-Type", contentType)

	// Set cache headers
	ss.setCacheHeaders(w, ext)

	// Serve file
	http.ServeFile(w, r, path)
}

// setCacheHeaders sets cache control headers
func (ss *StaticServer) setCacheHeaders(w http.ResponseWriter, ext string) {
	// Static assets can be cached longer
	switch ext {
	case ".js", ".css", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".woff", ".woff2", ".ttf":
		// Cache for 1 year
		w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
	case ".html", ".htm":
		// Don't cache HTML files
		w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
	default:
		// Cache for 1 hour
		w.Header().Set("Cache-Control", "public, max-age=3600")
	}
}

// serveDirectoryListing serves directory listing
func (ss *StaticServer) serveDirectoryListing(w http.ResponseWriter, r *http.Request, path string) {
	entries, err := os.ReadDir(path)
	if err != nil {
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	fmt.Fprintf(w, "<!DOCTYPE html><html><head><title>Directory Listing</title></head><body>")
	fmt.Fprintf(w, "<h1>Directory Listing for %s</h1>", r.URL.Path)
	fmt.Fprintf(w, "<ul>")

	for _, entry := range entries {
		name := entry.Name()
		url := filepath.Join(r.URL.Path, name)
		if entry.IsDir() {
			url += "/"
		}
		fmt.Fprintf(w, "<li><a href='%s'>%s</a></li>", url, name)
	}

	fmt.Fprintf(w, "</ul></body></html>")
}

// 3. SPA File Server

// SPAFileServer serves single-page applications
type SPAFileServer struct {
	root      string
	indexFile string
}

// NewSPAFileServer creates a new SPA file server
func NewSPAFileServer(root string) *SPAFileServer {
	return &SPAFileServer{
		root:      root,
		indexFile: "index.html",
	}
}

// ServeHTTP serves HTTP requests for SPA
func (spa *SPAFileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Clean path
	path := filepath.Join(spa.root, filepath.Clean(r.URL.Path))

	// Check if file exists
	if _, err := os.Stat(path); os.IsNotExist(err) {
		// File doesn't exist, serve index.html for SPA routing
		indexPath := filepath.Join(spa.root, spa.indexFile)
		http.ServeFile(w, r, indexPath)
		return
	}

	// Serve file if it exists
	info, err := os.Stat(path)
	if err != nil {
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		return
	}

	if info.IsDir() {
		indexPath := filepath.Join(path, spa.indexFile)
		http.ServeFile(w, r, indexPath)
	} else {
		http.ServeFile(w, r, path)
	}
}

// 4. File Server with ETag Support

// ETagFileServer serves files with ETag support
type ETagFileServer struct {
	root string
}

// NewETagFileServer creates a new ETag file server
func NewETagFileServer(root string) *ETagFileServer {
	return &ETagFileServer{root: root}
}

// generateETag generates ETag for file
func (ets *ETagFileServer) generateETag(path string, info os.FileInfo) string {
	data := fmt.Sprintf("%s-%d-%s", path, info.Size(), info.ModTime().Unix())
	hash := base64.StdEncoding.EncodeToString([]byte(data))
	return fmt.Sprintf("\"%s\"", hash)
}

// ServeHTTP serves HTTP requests with ETag
func (ets *ETagFileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	path := filepath.Join(ets.root, filepath.Clean(r.URL.Path))

	info, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			http.NotFound(w, r)
		} else {
			http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		}
		return
	}

	if info.IsDir() {
		path = filepath.Join(path, "index.html")
		info, _ = os.Stat(path)
	}

	// Generate ETag
	etag := ets.generateETag(path, info)

	// Check If-None-Match header
	if match := r.Header.Get("If-None-Match"); match == etag {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	// Set ETag header
	w.Header().Set("ETag", etag)

	// Serve file
	http.ServeFile(w, r, path)
}

// 5. File Server with Compression

// CompressionMiddleware compresses responses
type CompressionMiddleware struct {
	next http.Handler
}

// NewCompressionMiddleware creates compression middleware
func NewCompressionMiddleware(next http.Handler) *CompressionMiddleware {
	return &CompressionMiddleware{next: next}
}

// ShouldCompress checks if response should be compressed
func ShouldCompress(r *http.Request) bool {
	// Check Accept-Encoding header
	acceptEncoding := r.Header.Get("Accept-Encoding")
	return strings.Contains(acceptEncoding, "gzip")
}

// ServeHTTP serves compressed responses
func (cm *CompressionMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !ShouldCompress(r) {
		cm.next.ServeHTTP(w, r)
		return
	}

	// Check if content type is compressible
	path := r.URL.Path
	ext := filepath.Ext(path)
	compressible := map[string]bool{
		".js":  true,
		".css": true,
		".html": true,
		".txt": true,
		".xml": true,
		".json": true,
	}

	if !compressible[ext] {
		cm.next.ServeHTTP(w, r)
		return
	}

	// Set compression header
	w.Header().Set("Content-Encoding", "gzip")
	w.Header().Set("Vary", "Accept-Encoding")

	cm.next.ServeHTTP(w, r)
}

// 6. File Upload Handler

// UploadHandler handles file uploads
func UploadHandler(uploadDir string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost && r.Method != http.MethodPut {
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
			return
		}

		// Parse multipart form (max 32MB)
		err := r.ParseMultipartForm(32 << 20)
		if err != nil {
			http.Error(w, "Error parsing form", http.StatusBadRequest)
			return
		}

		// Get file from form
		file, handler, err := r.FormFile("file")
		if err != nil {
			http.Error(w, "Error retrieving file", http.StatusBadRequest)
			return
		}
		defer file.Close()

		// Create destination file
		dstPath := filepath.Join(uploadDir, handler.Filename)
		dst, err := os.Create(dstPath)
		if err != nil {
			http.Error(w, "Error creating file", http.StatusInternalServerError)
			return
		}
		defer dst.Close()

		// Copy file
		_, err = io.Copy(dst, file)
		if err != nil {
			http.Error(w, "Error saving file", http.StatusInternalServerError)
			return
		}

		w.WriteHeader(http.StatusCreated)
		fmt.Fprintf(w, "File uploaded successfully: %s", handler.Filename)
	}
}

// 7. File Download Handler

// DownloadHandler handles file downloads
func DownloadHandler(root string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// Get filename from query parameter
		filename := r.URL.Query().Get("file")
		if filename == "" {
			http.Error(w, "Filename required", http.StatusBadRequest)
			return
		}

		// Build file path
		path := filepath.Join(root, filepath.Base(filename))

		// Check if file exists
		if _, err := os.Stat(path); os.IsNotExist(err) {
			http.NotFound(w, r)
			return
		}

		// Set headers for download
		w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filepath.Base(filename)))
		w.Header().Set("Content-Type", "application/octet-stream")

		// Serve file
		http.ServeFile(w, r, path)
	}
}

// 8. Image Thumbnail Generator

// ThumbnailHandler generates and serves thumbnails
func ThumbnailHandler(imageDir string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// Get filename and size from query
		filename := r.URL.Query().Get("file")
		width := r.URL.Query().Get("width")
		height := r.URL.Query().Get("height")

		if filename == "" {
			http.Error(w, "Filename required", http.StatusBadRequest)
			return
		}

		// For simplicity, just serve the original file
		// In production, you'd use an image processing library
		path := filepath.Join(imageDir, filepath.Base(filename))

		if _, err := os.Stat(path); os.IsNotExist(err) {
			http.NotFound(w, r)
			return
		}

		log.Printf("Serving thumbnail: %s (size: %sx%s)", filename, width, height)
		http.ServeFile(w, r, path)
	}
}

// Usage Examples
func main() {
	fmt.Println("=== Web Go Static File Serving Examples ===\n")

	// 1. Basic file server
	fmt.Println("--- 1. Basic File Server ---")
	fs := http.FileServer(http.Dir("./public"))
	fmt.Printf("FileServer: %T\n", fs)

	// 2. Custom static server
	fmt.Println("\n--- 2. Custom Static Server ---")
	staticServer := NewStaticServer("./public")
	staticServer.AddHeader("X-Content-Type-Options", "nosniff")
	staticServer.AddHeader("X-Frame-Options", "DENY")
	fmt.Printf("StaticServer configured with %d custom headers\n", len(staticServer.headers))

	// 3. SPA file server
	fmt.Println("\n--- 3. SPA File Server ---")
	spaServer := NewSPAFileServer("./spa")
	fmt.Printf("SPAFileServer: %T\n", spaServer)

	// 4. ETag file server
	fmt.Println("\n--- 4. ETag File Server ---")
	etagServer := NewETagFileServer("./public")
	fmt.Printf("ETagFileServer: %T\n", etagServer)

	// 5. File upload handler
	fmt.Println("\n--- 5. File Upload Handler ---")
	uploadHandler := UploadHandler("./uploads")
	fmt.Printf("UploadHandler: %T\n", uploadHandler)

	// 6. Server configuration examples
	fmt.Println("\n--- 6. Server Configuration ---")

	// Example 1: Serve static files
	mux := http.NewServeMux()
	mux.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("./public"))))
	fmt.Println("Static files: /static/*")

	// Example 2: SPA route
	mux.Handle("/app/", spaServer)
	fmt.Println("SPA route: /app/*")

	// Example 3: Upload endpoint
	mux.HandleFunc("/upload", uploadHandler)
	fmt.Println("Upload endpoint: /upload")

	// Example 4: Download endpoint
	mux.HandleFunc("/download", DownloadHandler("./files"))
	fmt.Println("Download endpoint: /download?file=example.pdf")

	// Example 5: Thumbnail endpoint
	mux.HandleFunc("/thumbnail", ThumbnailHandler("./images"))
	fmt.Println("Thumbnail endpoint: /thumbnail?file=image.jpg&width=200&height=200")

	fmt.Println("\n--- 7. Server Setup ---")
	fmt.Println("http.ListenAndServe(':8080', mux)")

	fmt.Println("\n=== All Static File Serving Examples Completed ===")
}

💻 Manejo de Rutas go

🟡 intermediate ⭐⭐⭐

Enrutamiento y análisis de URLs usando paquete http Go y routers populares como gorilla/mux

⏱️ 25 min 🏷️ go, web, routing
Prerequisites: Intermediate Go, net/http package
// Web Go Route Handling Examples
// URL routing and parsing using Go http package

package main

import (
	"fmt"
	"net/http"
	"strings"
)

// 1. Basic HTTP Routing

// BasicRouteHandler demonstrates basic route handling
func BasicRouteHandler(w http.ResponseWriter, r *http.Request) {
	switch r.URL.Path {
	case "/":
		fmt.Fprintf(w, "Welcome to the Home Page!")
	case "/about":
		fmt.Fprintf(w, "About Us Page")
	case "/contact":
		fmt.Fprintf(w, "Contact Page")
	default:
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "404 - Page Not Found")
	}
}

// 2. Method-Based Routing

// MethodRouteHandler handles different HTTP methods
func MethodRouteHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodGet:
		fmt.Fprintf(w, "GET request received")
	case http.MethodPost:
		fmt.Fprintf(w, "POST request received")
	case http.MethodPut:
		fmt.Fprintf(w, "PUT request received")
	case http.MethodDelete:
		fmt.Fprintf(w, "DELETE request received")
	default:
		w.WriteHeader(http.StatusMethodNotAllowed)
		fmt.Fprintf(w, "Method Not Allowed")
	}
}

// 3. Query Parameter Handling

// QueryParamHandler handles query parameters
func QueryParamHandler(w http.ResponseWriter, r *http.Request) {
	queryParams := r.URL.Query()

	name := queryParams.Get("name")
	age := queryParams.Get("age")

	if name == "" {
		name = "Guest"
	}

	fmt.Fprintf(w, "Hello, %s!", name)
	if age != "" {
		fmt.Fprintf(w, " You are %s years old.", age)
	}
}

// 4. Path Parameter Extraction (Simple)

// UserHandler handles /users/:id pattern
func UserHandler(w http.ResponseWriter, r *http.Request) {
	// Extract ID from path like /users/123
	path := strings.TrimPrefix(r.URL.Path, "/users/")
	if path == "" || path == r.URL.Path {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "Invalid user ID")
		return
	}

	userID := strings.Split(path, "/")[0]
	fmt.Fprintf(w, "User ID: %s", userID)
}

// 5. Custom Route Structure

// Route represents a route definition
type Route struct {
	Name        string
	Method      string
	Pattern     string
	HandlerFunc http.HandlerFunc
}

// Routes is a collection of Route
type Routes []Route

// Router holds route definitions
type Router struct {
	routes Routes
}

// NewRouter creates a new router
func NewRouter() *Router {
	return &Router{
		routes: Routes{
			Route{
				Name:        "Index",
				Method:      "GET",
				Pattern:     "/",
				HandlerFunc: IndexHandler,
			},
			Route{
				Name:        "GetUser",
				Method:      "GET",
				Pattern:     "/users/{id}",
				HandlerFunc: GetUserHandler,
			},
			Route{
				Name:        "CreateUser",
				Method:      "POST",
				Pattern:     "/users",
				HandlerFunc: CreateUserHandler,
			},
		},
	}
}

// Handler returns the main HTTP handler
func (router *Router) Handler() http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		for _, route := range router.routes {
			if r.Method != route.Method {
				continue
			}

			// Simple pattern matching
			if matchRoute(route.Pattern, r.URL.Path) {
				route.HandlerFunc(w, r)
				return
			}
		}

		// No matching route found
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(w, "404 - Not Found")
	})
}

// matchRoute matches URL pattern to path
func matchRoute(pattern, path string) bool {
	// Replace {param} with wildcard
	pattern = strings.ReplaceAll(pattern, "{id}", "*")
	pattern = strings.ReplaceAll(pattern, "{name}", "*")

	// Simple matching
	if strings.HasSuffix(pattern, "*") {
		prefix := strings.TrimSuffix(pattern, "/*")
		return strings.HasPrefix(path, prefix)
	}

	return pattern == path
}

// Handler Functions

func IndexHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Welcome to the API!")
}

func GetUserHandler(w http.ResponseWriter, r *http.Request) {
	// Extract user ID from path
	path := strings.TrimPrefix(r.URL.Path, "/users/")
	userID := strings.Split(path, "/")[0]
	fmt.Fprintf(w, "Get User: %s", userID)
}

func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Create User")
}

// 6. Route Groups and Prefixes

// RouteGroup represents a group of routes with common prefix
type RouteGroup struct {
	prefix string
	routes []Route
}

// NewRouteGroup creates a new route group
func NewRouteGroup(prefix string) *RouteGroup {
	return &RouteGroup{
		prefix: prefix,
		routes: []Route{},
	}
}

// AddRoute adds a route to the group
func (rg *RouteGroup) AddRoute(method, pattern string, handler http.HandlerFunc) {
	fullPattern := rg.prefix + pattern
	rg.routes = append(rg.routes, Route{
		Method:      method,
		Pattern:     fullPattern,
		HandlerFunc: handler,
	})
}

// GetRoutes returns all routes in the group
func (rg *RouteGroup) GetRoutes() Routes {
	return rg.routes
}

// 7. URL Building

// URLBuilder builds URLs with parameters
type URLBuilder struct {
	baseURL string
}

// NewURLBuilder creates a new URL builder
func NewURLBuilder(baseURL string) *URLBuilder {
	return &URLBuilder{baseURL: baseURL}
}

// Build builds a URL with path
func (ub *URLBuilder) Build(path string) string {
	return ub.baseURL + path
}

// BuildWithParams builds a URL with path parameters
func (ub *URLBuilder) BuildWithParams(path string, params map[string]string) string {
	url := ub.baseURL + path
	for key, value := range params {
		url = strings.ReplaceAll(url, "{"+key+"}", value)
	}
	return url
}

// BuildWithQuery builds a URL with query parameters
func (ub *URLBuilder) BuildWithQuery(path string, params map[string]string) string {
	url := ub.baseURL + path
	if len(params) > 0 {
		url += "?"
		i := 0
		for key, value := range params {
			if i > 0 {
				url += "&"
			}
			url += fmt.Sprintf("%s=%s", key, value)
			i++
		}
	}
	return url
}

// 8. Request Context with Parameters

// Context holds request context data
type Context struct {
	Params map[string]string
	Query  map[string]string
}

// NewContext creates a new context from request
func NewContext(r *http.Request) *Context {
	ctx := &Context{
		Params: make(map[string]string),
		Query:  make(map[string]string),
	}

	// Parse query parameters
	for key, values := range r.URL.Query() {
		if len(values) > 0 {
			ctx.Query[key] = values[0]
		}
	}

	return ctx
}

// Usage Examples
func main() {
	fmt.Println("=== Web Go Route Handling Examples ===\n")

	// 1. Basic routing
	fmt.Println("--- 1. Basic Routing ---")
	fmt.Println("Routes: /, /about, /contact")
	fmt.Println("Handler: BasicRouteHandler")

	// 2. Method-based routing
	fmt.Println("\n--- 2. Method-Based Routing ---")
	fmt.Println("Methods: GET, POST, PUT, DELETE")
	fmt.Println("Handler: MethodRouteHandler")

	// 3. Query parameters
	fmt.Println("\n--- 3. Query Parameters ---")
	fmt.Println("URL: /greet?name=John&age=30")
	fmt.Println("Handler: QueryParamHandler")

	// 4. Custom router
	fmt.Println("\n--- 4. Custom Router ---")
	router := NewRouter()
	fmt.Printf("Registered %d routes\n", len(router.routes))
	for _, route := range router.routes {
		fmt.Printf("  %s %s - %s\n", route.Method, route.Pattern, route.Name)
	}

	// 5. Route groups
	fmt.Println("\n--- 5. Route Groups ---")
	apiGroup := NewRouteGroup("/api/v1")
	apiGroup.AddRoute("GET", "/users", IndexHandler)
	apiGroup.AddRoute("POST", "/users", CreateUserHandler)
	apiGroup.AddRoute("GET", "/users/{id}", GetUserHandler)
	fmt.Printf("API Group has %d routes\n", len(apiGroup.GetRoutes()))

	// 6. URL building
	fmt.Println("\n--- 6. URL Building ---")
	builder := NewURLBuilder("https://api.example.com")
	fmt.Printf("Home: %s\n", builder.Build("/"))
	fmt.Printf("User: %s\n", builder.BuildWithParams("/users/{id}", map[string]string{"id": "123"}))
	fmt.Printf("Search: %s\n", builder.BuildWithQuery("/search", map[string]string{"q": "golang", "limit": "10"}))

	// 7. Server setup example
	fmt.Println("\n--- 7. Server Setup ---")
	fmt.Println("http.HandleFunc('/', BasicRouteHandler)")
	fmt.Println("http.ListenAndServe(':8080', nil)")

	fmt.Println("\n=== All Route Handling Examples Completed ===")
}

💻 Middleware go

🟡 intermediate ⭐⭐⭐⭐

Middleware de procesamiento de solicitudes para registro, autenticación, CORS y controladores personalizados

⏱️ 30 min 🏷️ go, web, middleware
Prerequisites: Intermediate Go, net/http package, context package
// Web Go Middleware Examples
// Request processing middleware using Go http package

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strings"
	"time"
)

// 1. Basic Middleware Type

// Middleware is a function that wraps an http.Handler
type Middleware func(http.Handler) http.Handler

// 2. Logging Middleware

// LoggingMiddleware logs HTTP requests
func LoggingMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		// Create a custom response writer to capture status code
		lrw := &loggingResponseWriter{
			ResponseWriter: w,
			statusCode:     http.StatusOK,
		}

		// Call the next handler
		next.ServeHTTP(lrw, r)

		// Log the request
		log.Printf(
			"%s %s %s %d %s",
			r.Method,
			r.RequestURI,
			r.RemoteAddr,
			lrw.statusCode,
			time.Since(start),
		)
	})
}

// loggingResponseWriter wraps http.ResponseWriter to capture status code
type loggingResponseWriter struct {
	http.ResponseWriter
	statusCode int
}

func (lrw *loggingResponseWriter) WriteHeader(code int) {
	lrw.statusCode = code
	lrw.ResponseWriter.WriteHeader(code)
}

// 3. Authentication Middleware

// AuthMiddleware validates authentication tokens
func AuthMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token := r.Header.Get("Authorization")

		if token == "" {
			w.WriteHeader(http.StatusUnauthorized)
			json.NewEncoder(w).Encode(map[string]string{
				"error": "Authorization header required",
			})
			return
		}

		// Validate token (simplified)
		if !strings.HasPrefix(token, "Bearer ") {
			w.WriteHeader(http.StatusUnauthorized)
			json.NewEncoder(w).Encode(map[string]string{
				"error": "Invalid authorization format",
			})
			return
		}

		actualToken := strings.TrimPrefix(token, "Bearer ")
		if actualToken != "valid-token" {
			w.WriteHeader(http.StatusUnauthorized)
			json.NewEncoder(w).Encode(map[string]string{
				"error": "Invalid token",
			})
			return
		}

		// Add user context
		ctx := context.WithValue(r.Context(), "user", "authenticated-user")
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

// 4. CORS Middleware

// CORSMiddleware handles Cross-Origin Resource Sharing
func CORSMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Set CORS headers
		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")
		w.Header().Set("Access-Control-Max-Age", "86400")

		// Handle preflight requests
		if r.Method == http.MethodOptions {
			w.WriteHeader(http.StatusOK)
			return
		}

		next.ServeHTTP(w, r)
	})
}

// 5. Content Type Middleware

// ContentTypeMiddleware validates and sets content type
func ContentTypeMiddleware(expectedType string) Middleware {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// Check content type for POST/PUT requests
			if r.Method == http.MethodPost || r.Method == http.MethodPut {
				contentType := r.Header.Get("Content-Type")
				if contentType != expectedType {
					w.WriteHeader(http.StatusUnsupportedMediaType)
					json.NewEncoder(w).Encode(map[string]string{
						"error": fmt.Sprintf("Content-Type must be %s", expectedType),
					})
					return
				}
			}

			// Set response content type
			w.Header().Set("Content-Type", expectedType)
			next.ServeHTTP(w, r)
		})
	}
}

// 6. Recovery Middleware

// RecoveryMiddleware recovers from panics
func RecoveryMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if err := recover(); err != nil {
				log.Printf("Panic recovered: %v", err)
				w.WriteHeader(http.StatusInternalServerError)
				json.NewEncoder(w).Encode(map[string]string{
					"error": "Internal Server Error",
				})
			}
		}()

		next.ServeHTTP(w, r)
	})
}

// 7. Request ID Middleware

// RequestIDMiddleware adds unique request ID
func RequestIDMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		requestID := r.Header.Get("X-Request-ID")
		if requestID == "" {
			requestID = fmt.Sprintf("%d", time.Now().UnixNano())
		}

		w.Header().Set("X-Request-ID", requestID)
		ctx := context.WithValue(r.Context(), "requestID", requestID)

		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

// 8. Timeout Middleware

// TimeoutMiddleware adds timeout to requests
func TimeoutMiddleware(timeout time.Duration) Middleware {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ctx, cancel := context.WithTimeout(r.Context(), timeout)
			defer cancel()

			next.ServeHTTP(w, r.WithContext(ctx))
		})
	}
}

// 9. Rate Limiting Middleware

// RateLimiter tracks request counts
type RateLimiter struct {
	requests map[string][]time.Time
	limit    int
	window   time.Duration
}

// NewRateLimiter creates a new rate limiter
func NewRateLimiter(limit int, window time.Duration) *RateLimiter {
	rl := &RateLimiter{
		requests: make(map[string][]time.Time),
		limit:    limit,
		window:   window,
	}
	// Start cleanup goroutine
	go rl.cleanup()
	return rl
}

// Allow checks if request is allowed
func (rl *RateLimiter) Allow(ip string) bool {
	now := time.Now()
.rl.requests[ip] = append(rl.requests[ip], now)

	// Remove old requests
	var valid []time.Time
	for _, t := range rl.requests[ip] {
		if now.Sub(t) < rl.window {
			valid = append(valid, t)
		}
	}
	rl.requests[ip] = valid

	return len(rl.requests[ip]) <= rl.limit
}

// cleanup removes old entries
func (rl *RateLimiter) cleanup() {
	ticker := time.NewTicker(time.Minute)
	defer ticker.Stop()

	for range ticker.C {
		now := time.Now()
		for ip, times := range rl.requests {
			var valid []time.Time
			for _, t := range times {
				if now.Sub(t) < rl.window {
					valid = append(valid, t)
				}
			}
			if len(valid) == 0 {
				delete(rl.requests, ip)
			} else {
				rl.requests[ip] = valid
			}
		}
	}
}

// RateLimitMiddleware implements rate limiting
func RateLimitMiddleware(limiter *RateLimiter) Middleware {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ip := r.RemoteAddr

			if !limiter.Allow(ip) {
				w.WriteHeader(http.StatusTooManyRequests)
				json.NewEncoder(w).Encode(map[string]string{
					"error": "Rate limit exceeded",
				})
				return
			}

			next.ServeHTTP(w, r)
		})
	}
}

// 10. Middleware Chain

// Chain chains multiple middleware
func Chain(middlewares ...Middleware) Middleware {
	return func(next http.Handler) http.Handler {
		for i := len(middlewares) - 1; i >= 0; i-- {
			next = middlewares[i](next)
		}
		return next
	}
}

// 11. Example Handlers

func HomeHandler(w http.ResponseWriter, r *http.Request) {
	json.NewEncoder(w).Encode(map[string]string{
		"message": "Welcome Home!",
	})
}

func ProtectedHandler(w http.ResponseWriter, r *http.Request) {
	user := r.Context().Value("user")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"message": "This is a protected endpoint",
		"user":    user,
	})
}

func APIHandler(w http.ResponseWriter, r *http.Request) {
	json.NewEncoder(w).Encode(map[string]string{
		"message": "API endpoint",
	})
}

// Usage Examples
func main() {
	fmt.Println("=== Web Go Middleware Examples ===\n")

	// 1. Setup middlewares
	fmt.Println("--- 1. Available Middleware ---")
	fmt.Println("- LoggingMiddleware")
	fmt.Println("- AuthMiddleware")
	fmt.Println("- CORSMiddleware")
	fmt.Println("- RecoveryMiddleware")
	fmt.Println("- RequestIDMiddleware")

	// 2. Create middleware chain
	fmt.Println("\n--- 2. Middleware Chain ---")
	fmt.Println("Chain order matters:")
	fmt.Println("1. Recovery (outermost)")
	fmt.Println("2. Logging")
	fmt.Println("3. Request ID")
	fmt.Println("4. CORS")
	fmt.Println("5. Authentication")
	fmt.Println("6. Content Type")
	fmt.Println("7. Handler (innermost)")

	// 3. Example router with middleware
	fmt.Println("\n--- 3. Router with Middleware ---")

	// Create handlers
	mux := http.NewServeMux()
	mux.HandleFunc("/", HomeHandler)
	mux.HandleFunc("/protected", ProtectedHandler)
	mux.HandleFunc("/api", APIHandler)

	// Apply middleware chain
	rateLimiter := NewRateLimiter(10, time.Minute)

	handler := Chain(
		RecoveryMiddleware,
		LoggingMiddleware,
		RequestIDMiddleware,
		CORSMiddleware,
		RateLimitMiddleware(rateLimiter),
	)(mux)

	fmt.Printf("Server configured with middleware chain\n")

	// 4. Middleware examples
	fmt.Println("\n--- 4. Middleware Examples ---")

	// Content type middleware
	jsonMiddleware := ContentTypeMiddleware("application/json")
	fmt.Printf("Content-Type middleware created: %T\n", jsonMiddleware)

	// Timeout middleware
	timeoutMiddleware := TimeoutMiddleware(30 * time.Second)
	fmt.Printf("Timeout middleware created: %T\n", timeoutMiddleware)

	// 5. Server configuration
	fmt.Println("\n--- 5. Server Configuration ---")
	fmt.Println("http.ListenAndServe(':8080', handler)")

	// 6. Protected routes example
	fmt.Println("\n--- 6. Protected Routes ---")
	fmt.Println("Public routes: /")
	fmt.Println("Protected routes: /protected (requires auth)")
	fmt.Println("API routes: /api")

	fmt.Println("\n=== All Middleware Examples Completed ===")
}