Implementación y Uso de Middlewares en FastAPI
En FastAPI, los middlewares son componentes que se ejecutan antes y después de que cada solicitud sea manejada por un endpoint. Son útiles para tareas como autenticación, registro de solicitudes, manejo de errores, compresión y más.
1. ¿Qué es un Middleware?
Un middleware es una función o clase que intercepta las solicitudes HTTP entrantes y las respuestas salientes. Esto permite realizar operaciones personalizadas, como:
- Registrar logs de las solicitudes.
- Verificar tokens de autenticación.
- Modificar solicitudes o respuestas.
2. Crear un Middleware Básico
En FastAPI, los middlewares se implementan como clases que heredan de BaseHTTPMiddleware o se definen como funciones personalizadas.
Ejemplo básico: Registrar Logs
from fastapi import FastAPI, Request
from starlette.middleware.base import BaseHTTPMiddleware
app = FastAPI()
class LogMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
# Antes de procesar la solicitud
print(f"Request: {request.method} {request.url}")
# Procesar la solicitud
response = await call_next(request)
# Después de procesar la solicitud
print(f"Response status: {response.status_code}")
return response
# Agregar el middleware a la aplicación
app.add_middleware(LogMiddleware)
@app.get("/")
async def read_root():
return {"message": "Hello, World!"}
3. Uso de Middlewares Incorporados
FastAPI permite integrar middlewares ya existentes de Starlette o externos, como:
- CORS (Cross-Origin Resource Sharing): Para permitir que dominios externos accedan a la API.
- GZipMiddleware: Para comprimir respuestas grandes.
CORS Middleware
Permite configurar qué dominios pueden hacer solicitudes a tu API.
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["https://example.com"], # Dominios permitidos
allow_credentials=True,
allow_methods=["*"], # Métodos permitidos (GET, POST, etc.)
allow_headers=["*"], # Encabezados permitidos
)
@app.get("/")
async def read_root():
return {"message": "Hello, CORS!"}
GZip Middleware
Comprime las respuestas para mejorar el rendimiento.
from starlette.middleware.gzip import GZipMiddleware
app = FastAPI()
app.add_middleware(GZipMiddleware)
@app.get("/")
async def read_root():
return {"message": "This response is compressed!"}
4. Middleware Personalizado con Validaciones
Supongamos que deseas validar un encabezado personalizado en cada solicitud.
from fastapi import Request, HTTPException
from starlette.middleware.base import BaseHTTPMiddleware
class HeaderValidationMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
if "x-custom-header" not in request.headers:
raise HTTPException(status_code=400, detail="Missing x-custom-header")
# Pasar al siguiente middleware o endpoint
response = await call_next(request)
return response
app = FastAPI()
app.add_middleware(HeaderValidationMiddleware)
@app.get("/")
async def read_root():
return {"message": "Header is valid!"}
5. Orden de los Middlewares
El orden en el que los middlewares son añadidos es importante, ya que se ejecutan en secuencia. Por ejemplo:
app.add_middleware(MiddlewareA)
app.add_middleware(MiddlewareB)
En este caso:
MiddlewareA se ejecuta primero al procesar la solicitud.
MiddlewareB se ejecuta después al procesar la solicitud.
- Durante la respuesta, el orden es inverso:
MiddlewareB → MiddlewareA.
6. Middleware vs. Dependencias
Aunque los middlewares son globales y afectan todas las solicitudes, las dependencias en FastAPI ofrecen una forma más granular de validar o modificar solicitudes por endpoint.
Usa middlewares para tareas generales y dependencias para validaciones específicas.
7. Ejemplo Completo: Middleware de Tiempo de Ejecución
Calcula cuánto tiempo tarda cada solicitud en ser procesada.
import time
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
class TimerMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
app = FastAPI()
app.add_middleware(TimerMiddleware)
@app.get("/")
async def read_root():
return {"message": "Check the X-Process-Time header for timing info."}
Conclusión
Los middlewares en FastAPI son una herramienta poderosa para gestionar solicitudes y respuestas a nivel global. Puedes usarlos para optimizar tu API con tareas como autenticación, registro, compresión y validaciones. Asegúrate de combinarlos sabiamente con dependencias para una arquitectura eficiente.