Menu
Coddy logo textTech

Middleware como Decorator

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 97 de 107.

Middleware es una de las aplicaciones más prácticas del patrón Decorator en Go. En el desarrollo web, las funciones de middleware envuelven a los manejadores HTTP para añadir aspectos transversales como el registro (logging), la autenticación o el cronometraje (timing) sin modificar el manejador original.

La idea clave es que el middleware toma un manejador y devuelve un nuevo manejador con comportamiento añadido. Esto sigue exactamente el patrón Decorator: la misma interfaz de entrada y salida, con funcionalidad mejorada:

type Handler func(request string) string

func LoggingMiddleware(next Handler) Handler {
    return func(request string) string {
        result := next(request)
        return "[LOG] " + result
    }
}

func AuthMiddleware(next Handler) Handler {
    return func(request string) string {
        return "[AUTH] " + next(request)
    }
}

Cada middleware envuelve al siguiente manejador en la cadena, añadiendo su comportamiento antes o después de llamar al manejador envuelto. Puedes apilar múltiples middleware componiéndolos:

func MainHandler(request string) string {
    return "Response to: " + request
}

handler := LoggingMiddleware(AuthMiddleware(MainHandler))
fmt.Println(handler("GET /users"))
// [LOG] [AUTH] Response to: GET /users

El orden de envoltura determina el orden de ejecución: el middleware más externo se ejecuta primero. Este patrón se utiliza ampliamente en los frameworks web de Go para los pipelines de procesamiento de solicitudes, lo que facilita agregar o eliminar funcionalidades sin tocar la lógica central del manejador.

challenge icon

Desafío

Fácil

¡Vamos a construir un pipeline de procesamiento de solicitudes utilizando el patrón Middleware como Decorador! Crearás un sistema donde las funciones de middleware envuelven a los manejadores para añadir funcionalidades como temporización, validación y formato, todo sin modificar la lógica del manejador principal.

Organizarás tu código en tres archivos:

  • handler.go: Define tu tipo de manejador y el manejador base que procesa las solicitudes.

    Crea un tipo Handler como una función que recibe un string de solicitud y devuelve un string de respuesta:

    type Handler func(request string) string

    Implementa una función BaseHandler que devuelva Processed: [request] para cualquier solicitud que reciba.

  • middleware.go: Crea tus funciones de middleware que envuelven a los manejadores con comportamiento adicional.

    Construye tres funciones de middleware, cada una de las cuales recibe un Handler y devuelve un nuevo Handler:

    • TimingMiddleware — envuelve la respuesta con información de tiempo, devolviendo [TIMING] [response]
    • ValidationMiddleware — añade un prefijo de validación, devolviendo [VALID] [response]
    • UppercaseMiddleware — convierte toda la respuesta a mayúsculas

    Cada middleware debe llamar al manejador envuelto y mejorar su resultado.

  • main.go: Construye un pipeline de procesamiento componiendo los middleware.

    Lee el número de capas de middleware a aplicar. Luego, para cada capa, lee el tipo de middleware (timing, validation, o uppercase) y envuelve tu manejador actual con ese middleware. Aplícalos en el orden en que se leen: el primer middleware leído se convierte en el envoltorio más externo.

    Después de construir el pipeline, lee el string de la solicitud, procésalo a través de tu manejador compuesto e imprime el resultado.

Se proporcionarán las siguientes entradas:

  • Línea 1: Número de capas de middleware
  • Líneas siguientes: Un tipo de middleware por línea (timing, validation, o uppercase)
  • Última línea: La solicitud a procesar

Por ejemplo, dado:

2
timing
validation
GET /users

Tu salida debería ser:

[TIMING] [VALID] Processed: GET /users

Y dado:

3
uppercase
timing
validation
POST /data

Tu salida debería ser:

[TIMING] [VALID] PROCESSED: POST /DATA

Y dado:

1
uppercase
hello world

Tu salida debería ser:

PROCESSED: HELLO WORLD

Y dado:

0
simple request

Tu salida debería ser:

Processed: simple request

Observa cómo el orden de envoltura del middleware afecta a la salida: el primer middleware aplicado (el más externo) se ejecuta primero y ve el resultado final de todos los middleware internos. El middleware de mayúsculas en el segundo ejemplo se ejecuta al último (el más interno), por lo que convierte a mayúsculas la respuesta base antes de que la temporización y la validación añadan sus prefijos.

Pruébalo tú mismo

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Leer el número de capas de middleware
	nLine, _ := reader.ReadString('\n')
	n, _ := strconv.Atoi(strings.TrimSpace(nLine))

	// Leer los tipos de middleware
	middlewareTypes := make([]string, n)
	for i := 0; i < n; i++ {
		line, _ := reader.ReadString('\n')
		middlewareTypes[i] = strings.TrimSpace(line)
	}

	// Leer la solicitud
	request, _ := reader.ReadString('\n')
	request = strings.TrimSpace(request)

	// TODO: Comenzar con el BaseHandler

	// TODO: Construir el pipeline envolviendo el handler con middleware
	// Aplicar el middleware en orden - el primer middleware leído se convierte en el envoltorio más externo
	// Hint: Recorrer middlewareTypes en orden INVERSO para lograr esto
	// El último middleware aplicado será el más externo (se ejecuta primero)

	// TODO: Procesar la solicitud a través del handler compuesto e imprimir el resultado
	fmt.Println("TODO: implement pipeline")
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos