🧩 Middleware - Diseño¶
Estructura del middleware.¶
Básicamente está formado por 2 funciones dentro del mismo prg:
- Setup : Alguna variable que definimos por defecto y podamos reconfigurar si es necesario
- Middleware: funcion que ejecutará el proceso y podrá usar la variable del setup
Este podria ser el diseño de un middleware
STATIC s_MyVar := 3600
FUNCTION Mw_FenixSetup( nExpSecs )
IF ValType( nExpSecs ) == "N" .AND. nExpSecs > 0
s_MyVar := nExpSecs
ENDIF
RETURN nil
FUNCTION Mw_Fenix( oCtx )
LOCAL lAccept := .T.
...
// Uso de s_MyVar
...
RETURN lAccept
Dinámica de diseño según el tipo de aplicación¶
No todas las aplicaciones necesitan los mismos middlewares. La elección depende de quién consume la API y cómo.
Aqui ponemos algun ejemplo de diseño sgún el escenario.
Aplicación Web clásica (HTML + formularios)¶
El usuario interactúa desde un navegador. El estado se guarda en sesión con cookie.
Petición navegador
│
├─ MW_BodyLimit ← limitar tamaño (uploads)
├─ MW_Session ← cargar sesión desde cookie
├─ MW_Csrf ← verificar token CSRF en POST/PUT/DELETE
├─ MW_RequireAuth ← ¿hay sesión activa?
└─ MW_RequireRole ← ¿tiene el rol necesario?
El orden importa: primero cargar la sesión, luego verificar CSRF (que necesita la sesión), y solo después comprobar autenticación.
API REST (JSON, cliente externo)¶
El cliente es una app móvil, SPA o servicio externo. No hay cookies de sesión: la autenticación es stateless con JWT o API Key.
Petición cliente API
│
├─ MW_Cors ← cabeceras CORS para el navegador
├─ MW_RateLimit ← protección contra abuso
├─ MW_BodyLimit ← limitar tamaño del body
├─ MW_Jwt ← validar Bearer token
├─ MW_RequireAuth ← ¿token válido?
└─ MW_RequireRole ← ¿rol suficiente?
Servicio interno (machine-to-machine)¶
Comunicación entre servicios del mismo sistema. Sin usuarios humanos, sin sesiones. La autenticación es por API Key estática.
Petición servicio interno
│
├─ MW_BodyLimit ← protección básica
├─ MW_ApiKey ← validar X-Api-Key
└─ MW_RequireAuth ← ¿clave conocida?
Qué proteger y qué no¶
Proteger siempre¶
| ¿Qué? | ¿Con qué? |
|---|---|
| Rutas privadas (panel, datos de usuario) | Autenticación + roles |
| Endpoints que modifican datos (POST/PUT/DELETE) | CSRF en web, JWT/ApiKey en API |
| Subida de ficheros o payloads grandes | Límite de body |
| Endpoints públicos con alto tráfico | Rate limiting |
| Cualquier cosa que devuelva datos sensibles | Cabeceras de seguridad HTTP |
No sobreproteger¶
Un error común es aplicar todos los middlewares a todas las rutas por precaución. El resultado es latencia añadida innecesaria y código más difícil de depurar.
La página de bienvenida pública no necesita JWT. Un endpoint de health-check no necesita sesión. Los assets estáticos no necesitan CSRF.
La regla es sencilla: aplica el middleware mínimo necesario para el nivel de confianza que requiere esa ruta.
Ejemplos conceptuales¶
Ejemplo simple: registro de peticiones¶
El middleware más sencillo posible no bloquea nada. Solo observa y registra:
Petición llega
│
▼
[MW_ReqLog]
Anota: método + path + IP en el log
Devuelve .T. siempre
│
▼
Handler - ejecuta normalmente
Útil para trazabilidad: saber qué rutas se llaman, con qué frecuencia, desde qué IPs.
Ejemplo compuesto: ruta de API protegida¶
Una ruta de API que solo pueden usar usuarios autenticados con rol editor:
POST /api/articles (crear artículo)
│
▼
[MW_RateLimit]
¿Esta IP ha superado 100 req/min?
No → .T., continúa
│
▼
[MW_Jwt]
¿Hay cabecera Authorization: Bearer xxx?
¿El token es válido y no ha expirado?
Sí → deposita payload en oCtx:hData["jwt"] → .T.
No → responde 401 → .F. → corta
│
▼
[MW_RequireRole("editor")]
¿oCtx:hData["jwt"]["role"] == "editor"?
Sí → .T.
No → responde 403 → .F. → corta
│
▼
Handler _CreateArticle()
Ya sabe que el usuario es válido y tiene permiso.
Solo se ocupa de crear el artículo.
Tres middlewares, tres responsabilidades claras, código de negocio limpio.