¿Qué es middleware en Go?
Un middleware es una función que envuelve un http.Handler para añadir funcionalidad antes y/o después de que el handler principal procese la petición. La interfaz clave es:

type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
// Patrón de middleware: función que recibe y devuelve http.Handler
type Middleware func(http.Handler) http.Handler
Enrutamiento mejorado en Go 1.22
Go 1.22 mejoró significativamente http.ServeMux para soportar métodos HTTP y comodines en los patrones:
mux := http.NewServeMux()
// Método + ruta exacta
mux.HandleFunc("GET /api/productos", listarProductos)
mux.HandleFunc("POST /api/productos", crearProducto)
// Parámetro de ruta con {nombre}
mux.HandleFunc("GET /api/productos/{id}", obtenerProducto)
mux.HandleFunc("PUT /api/productos/{id}", actualizarProducto)
mux.HandleFunc("DELETE /api/productos/{id}", eliminarProducto)
// Extraer el parámetro en el handler
func obtenerProducto(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id") // disponible desde Go 1.22
// ...
}
Middleware de logging
func middlewareLogging(logger *slog.Logger) func(http.Handler) http.Handler {
return func(siguiente http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
inicio := time.Now()
// Capturar el status code con un ResponseWriter personalizado
rw := &responseWriter{ResponseWriter: w, status: http.StatusOK}
siguiente.ServeHTTP(rw, r)
logger.Info("petición HTTP",
slog.String("método", r.Method),
slog.String("ruta", r.URL.Path),
slog.Int("status", rw.status),
slog.Duration("duración", time.Since(inicio)),
slog.String("ip", r.RemoteAddr),
)
})
}
}
// ResponseWriter que captura el status code
type responseWriter struct {
http.ResponseWriter
status int
}
func (rw *responseWriter) WriteHeader(status int) {
rw.status = status
rw.ResponseWriter.WriteHeader(status)
}
Middleware de recuperación de pánicos
func middlewareRecuperacion(logger *slog.Logger) func(http.Handler) http.Handler {
return func(siguiente http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
logger.Error("pánico recuperado",
slog.Any("error", err),
slog.String("ruta", r.URL.Path),
)
http.Error(w, "Error interno del servidor",
http.StatusInternalServerError)
}
}()
siguiente.ServeHTTP(w, r)
})
}
}
Middleware de CORS
func middlewareCORS(originesPermitidos []string) func(http.Handler) http.Handler {
origenSet := make(map[string]bool)
for _, o := range originesPermitidos {
origenSet[o] = true
}
return func(siguiente http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origen := r.Header.Get("Origin")
if origenSet[origen] || origenSet["*"] {
w.Header().Set("Access-Control-Allow-Origin", origen)
w.Header().Set("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers",
"Content-Type, Authorization")
}
// Responder a peticiones preflight OPTIONS
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
siguiente.ServeHTTP(w, r)
})
}
}
Encadenar middlewares
// Función de composición que aplica middlewares de derecha a izquierda
func encadenar(h http.Handler, middlewares ...func(http.Handler) http.Handler) http.Handler {
for i := len(middlewares) - 1; i >= 0; i-- {
h = middlewares[i](h)
}
return h
}
// Uso
func main() {
logger := slog.Default()
mux := http.NewServeMux()
// Registrar handlers...
mux.HandleFunc("GET /api/productos", listarProductos)
// Aplicar middleware a todo el servidor
servidor := encadenar(mux,
middlewareRecuperacion(logger),
middlewareLogging(logger),
middlewareCORS([]string{"https://miapp.com"}),
)
http.ListenAndServe(":8080", servidor)
}
Helpers para respuestas JSON
func responderJSON(w http.ResponseWriter, status int, datos any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
if err := json.NewEncoder(w).Encode(datos); err != nil {
slog.Error("error al codificar respuesta JSON", "err", err)
}
}
func responderError(w http.ResponseWriter, status int, mensaje string) {
responderJSON(w, status, map[string]string{"error": mensaje})
}
// Uso en handlers
func listarProductos(w http.ResponseWriter, r *http.Request) {
productos, err := repo.ObtenerTodos(r.Context())
if err != nil {
responderError(w, http.StatusInternalServerError, "error al obtener productos")
return
}
responderJSON(w, http.StatusOK, productos)
}
func crearProducto(w http.ResponseWriter, r *http.Request) {
var p Producto
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
responderError(w, http.StatusBadRequest, "JSON inválido")
return
}
id, err := repo.Crear(r.Context(), p)
if err != nil {
responderError(w, http.StatusInternalServerError, "error al crear producto")
return
}
p.ID = id
responderJSON(w, http.StatusCreated, p)
}
Servidor HTTP completo con contexto y apagado limpio
func NuevoServidor(repo ProductoRepositorio, logger *slog.Logger) *http.Server {
mux := http.NewServeMux()
mux.HandleFunc("GET /api/productos", listarProductos(repo))
mux.HandleFunc("POST /api/productos", crearProducto(repo))
mux.HandleFunc("GET /api/productos/{id}", obtenerProducto(repo))
handler := encadenar(mux,
middlewareRecuperacion(logger),
middlewareLogging(logger),
)
return &http.Server{
Addr: ":8080",
Handler: handler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
}
}
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Go es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Go
Explora más contenido relacionado con Go y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Implementar middleware como http.Handler decoradores en Go. Encadenar múltiples middlewares con funciones de composición. Crear middleware de logging, recuperación de pánico y autenticación básica. Usar los patrones de enrutamiento mejorados de Go 1.22 (métodos y wildcards en patrones). Gestionar cabeceras CORS con middleware. Servir respuestas JSON con helpers reutilizables.
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje