middleware

package
v1.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2025 License: MIT Imports: 10 Imported by: 0

README

Middleware

Overview

The Middleware component provides a collection of HTTP middleware for Go web applications. It offers ready-to-use middleware for common tasks like request logging, error handling, timeout management, and CORS support. The component also includes utilities for middleware chaining and context management.

Features

  • Request Context: Add request IDs and timing information to request contexts
  • Logging: Log request details including method, path, status code, and duration
  • Error Handling: Map errors to appropriate HTTP responses with status codes
  • Panic Recovery: Catch and handle panics to prevent application crashes
  • Timeout Management: Add request timeouts with proper cancellation handling
  • CORS Support: Add Cross-Origin Resource Sharing headers to responses
  • Context Cancellation: Detect and handle client disconnections
  • Middleware Chaining: Apply multiple middleware in a specific order
  • Thread Safety: Thread-safe response writing for concurrent operations

Installation

go get github.com/abitofhelp/servicelib/middleware

Quick Start

package main

import (
    "net/http"
    
    "github.com/abitofhelp/servicelib/middleware"
    "go.uber.org/zap"
)

func main() {
    // Create a logger
    logger, _ := zap.NewProduction()
    
    // Create a simple handler
    handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello, World!"))
    })
    
    // Apply middleware
    wrappedHandler := middleware.ApplyMiddleware(handler, logger)
    
    // Start the server
    http.ListenAndServe(":8080", wrappedHandler)
}

API Documentation

Core Types
Middleware

Represents a middleware function that wraps an http.Handler.

type Middleware func(http.Handler) http.Handler
Key Functions
Chain

Applies multiple middleware to an http.Handler in the order they are provided.

func Chain(handler http.Handler, middlewares ...Middleware) http.Handler
ApplyMiddleware

Applies all middleware to a handler in the recommended order.

func ApplyMiddleware(handler http.Handler, logger *zap.Logger) http.Handler
Individual Middleware
WithRequestContext

Adds request context information to the request.

func WithRequestContext(next http.Handler) http.Handler
WithLogging

Adds request logging.

func WithLogging(logger *logging.ContextLogger, next http.Handler) http.Handler
WithRecovery

Adds panic recovery to the request.

func WithRecovery(logger *logging.ContextLogger, next http.Handler) http.Handler
WithTimeout

Adds a timeout to the request context.

func WithTimeout(timeout time.Duration) func(http.Handler) http.Handler
WithErrorHandling

Adds centralized error handling.

func WithErrorHandling(next http.Handler) http.Handler
WithCORS

Adds CORS headers to allow cross-origin requests.

func WithCORS(next http.Handler) http.Handler
WithContextCancellation

Checks for context cancellation and detects when a client disconnects.

func WithContextCancellation(logger *logging.ContextLogger) func(next http.Handler) http.Handler
Context Utilities
RequestID

Returns the request ID from the context.

func RequestID(ctx context.Context) string
StartTime

Returns the request start time from the context.

func StartTime(ctx context.Context) time.Time
RequestDuration

Returns the duration since the request started.

func RequestDuration(ctx context.Context) time.Duration

Examples

For complete, runnable examples, see the following directories in the EXAMPLES directory:

Best Practices

  1. Apply Middleware in the Right Order: The order of middleware matters; typically, request context should be first, followed by recovery, logging, etc.
  2. Use Chain for Custom Middleware Combinations: Use the Chain function when you need a specific combination of middleware
  3. Handle Context Cancellation: Always handle context cancellation to avoid resource leaks
  4. Set Appropriate Timeouts: Configure timeouts based on expected operation durations
  5. Log Request Details: Use the logging middleware to track request details for debugging and monitoring

Troubleshooting

Common Issues
Middleware Order Problems

If middleware isn't working as expected:

  • Check the order in which middleware is applied
  • Remember that middleware is executed in reverse order of application (last applied is first executed)
  • Use the ApplyMiddleware function for a standard ordering
Response Already Written

If you're seeing "http: multiple response.WriteHeader calls":

  • Ensure only one middleware is writing the response
  • Use the error handling middleware to centralize error responses
  • Check for race conditions in concurrent handlers
  • Logging - Logging integration for middleware
  • Errors - Error handling used by middleware
  • Context - Context utilities used by middleware
  • Health - Health checks that can use middleware

Contributing

Contributions to this component are welcome! Please see the Contributing Guide for more information.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Overview

Package middleware provides a collection of HTTP middleware for Go web applications.

Middleware are functions that wrap HTTP handlers to add functionality such as request logging, error handling, timeout management, and CORS support. This package offers ready-to-use middleware for common tasks and utilities for middleware chaining and context management.

Key features:

  • Request Context: Add request IDs and timing information to request contexts
  • Logging: Log request details including method, path, status code, and duration
  • Error Handling: Map errors to appropriate HTTP responses with status codes
  • Panic Recovery: Catch and handle panics to prevent application crashes
  • Timeout Management: Add request timeouts with proper cancellation handling
  • CORS Support: Add Cross-Origin Resource Sharing headers to responses
  • Context Cancellation: Detect and handle client disconnections
  • Middleware Chaining: Apply multiple middleware in a specific order
  • Thread Safety: Thread-safe response writing for concurrent operations

The package provides both individual middleware functions and utilities for combining them. The ApplyMiddleware function applies a standard set of middleware in the recommended order, while the Chain function allows for custom combinations.

Example usage:

// Create a logger
logger, _ := zap.NewProduction()

// Create a simple handler
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello, World!"))
})

// Apply middleware
wrappedHandler := middleware.ApplyMiddleware(handler, logger)

// Start the server
http.ListenAndServe(":8080", wrappedHandler)

For more advanced usage, middleware can be applied individually or chained in a custom order:

// Create a logger
logger, _ := zap.NewProduction()
contextLogger := logging.NewContextLogger(logger)

// Apply middleware in a custom order
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello, World!"))
})

// Chain middleware in a specific order
wrappedHandler := middleware.Chain(handler,
    middleware.WithRequestID(context.Background()),
    middleware.Logging(contextLogger),
    middleware.Recovery(contextLogger),
)

// Start the server
http.ListenAndServe(":8080", wrappedHandler)

The order of middleware application is important, as it determines the order of execution. Middleware is executed in reverse order of application, so the first middleware applied is the outermost wrapper (first to process the request, last to process the response).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ApplyMiddleware

func ApplyMiddleware(handler http.Handler, logger *zap.Logger) http.Handler

ApplyMiddleware applies all middleware to a handler in the recommended order. This function provides a consistent way to apply middleware across different applications. It applies the following middleware in order (outermost first):

  1. WithRequestContext - Adds request ID and timing information
  2. WithCORS - Adds CORS headers for cross-origin requests
  3. WithRecovery - Catches panics and converts them to 500 responses
  4. WithContextCancellation - Handles client disconnections
  5. WithLogging - Logs request details
  6. WithErrorHandling - Maps errors to appropriate HTTP responses

The order is important for proper middleware chaining. For example, the request context middleware is applied first so that all other middleware can access the request ID. The recovery middleware is applied early to catch panics in other middleware.

Parameters:

  • handler: The HTTP handler to wrap with middleware.
  • logger: Logger for recording middleware events.

Returns:

  • A wrapped HTTP handler with all middleware applied in the recommended order.

func Chain added in v1.4.0

func Chain(handler http.Handler, middlewares ...Middleware) http.Handler

Chain applies multiple middleware to a http.Handler in the order they are provided. The first middleware in the list becomes the outermost wrapper, meaning it is the first to process the incoming request and the last to process the outgoing response.

The middleware are applied in reverse order (from last to first) to achieve this, because each middleware wraps the handler that comes after it in the chain.

Parameters:

  • handler: The base HTTP handler to wrap with middleware.
  • middlewares: A variadic list of middleware functions to apply.

Returns:

  • An http.Handler with all the middleware applied in the specified order.

Example:

handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello, World!"))
})

// Apply middleware in order: logging, then recovery, then request ID
wrappedHandler := middleware.Chain(handler,
    middleware.WithRequestID(context.Background()),
    middleware.Recovery(logger),
    middleware.Logging(logger),
)

func RequestDuration

func RequestDuration(ctx context.Context) time.Duration

RequestDuration returns the duration since the request started. This function calculates how long a request has been processing by comparing the start time from the context with the current time. If the start time is not found or is a zero time, a duration of 0 is returned.

Parameters:

  • ctx: The context containing the request start time.

Returns:

  • A time.Duration representing how long the request has been processing.

func RequestID

func RequestID(ctx context.Context) string

RequestID returns the request ID from the context. This function safely extracts the request ID from the context, handling nil contexts and type assertions. If the context is nil or doesn't contain a request ID, an empty string is returned.

Parameters:

  • ctx: The context from which to extract the request ID.

Returns:

  • A string containing the request ID, or an empty string if not found.

func StartTime

func StartTime(ctx context.Context) time.Time

StartTime returns the request start time from the context. This function safely extracts the start time from the context, handling nil contexts and type assertions. If the context is nil or doesn't contain a start time, a zero time is returned.

Parameters:

  • ctx: The context from which to extract the start time.

Returns:

  • A time.Time representing when the request started, or a zero time if not found.

func WithCORS

func WithCORS(next http.Handler) http.Handler

WithCORS adds CORS headers to allow cross-origin requests

func WithContextCancellation

func WithContextCancellation(logger *logging.ContextLogger) func(next http.Handler) http.Handler

WithContextCancellation is a middleware that checks for context cancellation It detects when a client disconnects and logs the event

func WithErrorHandling

func WithErrorHandling(next http.Handler) http.Handler

WithErrorHandling adds centralized error handling

func WithLogging

func WithLogging(logger *logging.ContextLogger, next http.Handler) http.Handler

WithLogging adds request logging. This middleware logs information about each request, including the HTTP method, path, status code, and duration. It uses a response writer wrapper to capture the status code of the response.

The logging occurs after the request has been processed, so it includes the final status code and the total time taken to process the request. This is useful for monitoring and debugging.

Parameters:

  • logger: A context logger for logging request details.
  • next: The next handler in the middleware chain.

Returns:

  • An http.Handler that wraps the next handler with logging functionality.

func WithRecovery

func WithRecovery(logger *logging.ContextLogger, next http.Handler) http.Handler

WithRecovery adds panic recovery to the request. This middleware catches any panics that occur during request processing and converts them into 500 Internal Server Error responses. It also logs the panic details, including the stack trace, to help with debugging.

Without this middleware, a panic in a handler would crash the entire server. With it, the panic is contained to just the current request, allowing the server to continue handling other requests.

Parameters:

  • logger: A context logger for logging panic details.
  • next: The next handler in the middleware chain.

Returns:

  • An http.Handler that wraps the next handler with panic recovery functionality.

func WithRequestContext

func WithRequestContext(next http.Handler) http.Handler

WithRequestContext adds request context information to the request. This middleware adds a unique request ID and start time to the request context. If the request already has an X-Request-ID header, that value is used as the request ID; otherwise, a new unique ID is generated. The request ID is also added to the response headers for correlation.

This middleware is typically one of the first in the chain, as it provides context information that other middleware and handlers can use.

Parameters:

  • next: The next handler in the middleware chain.

Returns:

  • An http.Handler that wraps the next handler with request context functionality.

func WithTimeout

func WithTimeout(timeout time.Duration) func(http.Handler) http.Handler

WithTimeout adds a timeout to the request context. This middleware sets a maximum duration for the request to complete. If the request takes longer than the specified timeout, it is canceled and a 504 Gateway Timeout response is returned to the client.

The middleware uses a goroutine to process the request and a select statement to wait for either the request to complete or the timeout to expire. It uses a synchronized response writer to avoid race conditions when writing the response.

Parameters:

  • timeout: The maximum duration allowed for the request to complete.

Returns:

  • A middleware function that adds timeout functionality to an HTTP handler.

Types

type ContextKey

type ContextKey string

ContextKey is a type for context keys to avoid collisions. Using a custom type for context keys helps prevent key collisions when multiple packages store values in the context.

const (
	// RequestIDKey is the key for the request ID in the context.
	// This key is used to store and retrieve the unique identifier for each request.
	RequestIDKey ContextKey = "request_id"

	// StartTimeKey is the key for the request start time in the context.
	// This key is used to store and retrieve the time when the request started processing.
	StartTimeKey ContextKey = "start_time"

	// UserIDKey is the key for the user ID in the context (for future auth).
	// This key is used to store and retrieve the ID of the authenticated user.
	UserIDKey ContextKey = "user_id"
)

type Middleware added in v1.4.0

type Middleware func(http.Handler) http.Handler

Middleware represents a middleware function that wraps an http.Handler. A middleware takes an http.Handler and returns a new http.Handler that adds some functionality before and/or after calling the original handler.

This type allows middleware to be composed and chained together in a flexible way.

func Logging added in v1.4.0

func Logging(logger *logging.ContextLogger) Middleware

Logging is a middleware that logs request information. This is a convenience wrapper around WithLogging that returns a Middleware function, making it easier to use with the Chain function.

Parameters:

  • logger: A context logger for logging request details.

Returns:

  • A Middleware function that adds logging functionality.

func Recovery added in v1.4.0

func Recovery(logger *logging.ContextLogger) Middleware

Recovery is a middleware that recovers from panics. This is a convenience wrapper around WithRecovery that returns a Middleware function, making it easier to use with the Chain function.

Parameters:

  • logger: A context logger for logging panic details.

Returns:

  • A Middleware function that adds panic recovery functionality.

func WithRequestID added in v1.4.0

func WithRequestID(ctx context.Context) Middleware

WithRequestID is a middleware that adds a request ID to the context. This is a convenience wrapper around WithRequestContext that returns a Middleware function, making it easier to use with the Chain function.

Parameters:

  • ctx: The parent context to use (typically context.Background()).

Returns:

  • A Middleware function that adds request ID functionality.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL