En el desarrollo web, el middleware es una pieza de software que se encuentra entre el servidor y las aplicaciones, gestionando las solicitudes y respuestas HTTP. En Go, el middleware se utiliza para realizar tareas comunes como la autenticación, el registro de logs, la gestión de sesiones y la manipulación de solicitudes/respuestas antes de que lleguen a los controladores finales.

Conceptos Clave

  1. Definición de Middleware: Funciones que se ejecutan antes o después de las solicitudes HTTP.
  2. Encadenamiento de Middleware: La capacidad de combinar múltiples middleware para procesar una solicitud.
  3. Contexto de Solicitud: Cómo pasar datos entre middleware y controladores.

Ejemplo Práctico

Paso 1: Configuración Básica

Primero, asegúrate de tener un servidor web básico en Go. Aquí hay un ejemplo simple:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, World!")
    })

    http.ListenAndServe(":8080", nil)
}

Paso 2: Creación de un Middleware

Vamos a crear un middleware que registre cada solicitud entrante:

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
)

// LoggerMiddleware es un middleware que registra la solicitud entrante
func LoggerMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        log.Printf("Started %s %s", r.Method, r.URL.Path)

        next.ServeHTTP(w, r)

        log.Printf("Completed %s in %v", r.URL.Path, time.Since(start))
    })
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, World!")
    })

    // Envolvemos el mux con el middleware
    loggedMux := LoggerMiddleware(mux)

    http.ListenAndServe(":8080", loggedMux)
}

Paso 3: Encadenamiento de Middleware

Podemos encadenar múltiples middleware para realizar diferentes tareas. Aquí hay un ejemplo que agrega un middleware de autenticación:

// AuthMiddleware es un middleware que verifica la autenticación
func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        if token != "valid-token" {
            http.Error(w, "Forbidden", http.StatusForbidden)
            return
        }
        next.ServeHTTP(w, r)
    })
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, World!")
    })

    // Encadenamos los middleware
    loggedMux := LoggerMiddleware(mux)
    authMux := AuthMiddleware(loggedMux)

    http.ListenAndServe(":8080", authMux)
}

Explicación del Código

  1. LoggerMiddleware: Registra la solicitud entrante y el tiempo que tarda en completarse.
  2. AuthMiddleware: Verifica si la solicitud tiene un token de autorización válido. Si no es así, responde con un error 403 (Forbidden).
  3. Encadenamiento: Los middleware se encadenan envolviendo el mux con LoggerMiddleware y luego envolviendo el resultado con AuthMiddleware.

Ejercicio Práctico

Ejercicio 1: Middleware de Compresión

Crea un middleware que comprima las respuestas HTTP utilizando gzip.

Pistas:

  • Usa el paquete compress/gzip.
  • Modifica el ResponseWriter para escribir la respuesta comprimida.

Solución

package main

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

// GzipMiddleware es un middleware que comprime las respuestas HTTP
func GzipMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
            next.ServeHTTP(w, r)
            return
        }

        w.Header().Set("Content-Encoding", "gzip")
        gz := gzip.NewWriter(w)
        defer gz.Close()

        gzipWriter := gzipResponseWriter{Writer: gz, ResponseWriter: w}
        next.ServeHTTP(gzipWriter, r)
    })
}

type gzipResponseWriter struct {
    http.ResponseWriter
    *gzip.Writer
}

func (w gzipResponseWriter) Write(b []byte) (int, error) {
    return w.Writer.Write(b)
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, World!")
    })

    // Encadenamos los middleware
    loggedMux := LoggerMiddleware(mux)
    authMux := AuthMiddleware(loggedMux)
    gzipMux := GzipMiddleware(authMux)

    http.ListenAndServe(":8080", gzipMux)
}

Explicación de la Solución

  1. GzipMiddleware: Verifica si el cliente acepta respuestas gzip. Si es así, modifica el ResponseWriter para comprimir la respuesta.
  2. gzipResponseWriter: Un ResponseWriter personalizado que escribe datos comprimidos.

Conclusión

El middleware es una herramienta poderosa en el desarrollo web con Go. Permite la separación de preocupaciones y la reutilización de código para tareas comunes como el registro de logs, la autenticación y la compresión de respuestas. Al dominar el uso de middleware, puedes construir aplicaciones web más robustas y mantenibles.

En el siguiente tema, exploraremos cómo trabajar con plantillas en Go para generar contenido HTML dinámico.

© Copyright 2024. Todos los derechos reservados