IR
irwinrodriguez.dev
Volver a documentacion

Middleware

El middleware en FoxServer son componentes que procesan las peticiones antes o despues de que lleguen al controlador. Puedes usar los integrados (JWT, CORS, logging) o escribir los tuyos propios.

JWT — Autenticacion

El middleware JWT protege endpoints privados. Los clientes deben enviar un token valido en el header Authorization.

Configuracion en JSON:

"middleware": {
  "auth": {
    "enabled": true,
    "type": "jwt",
    "secret": "your-256-bit-secret-key-base64-encoded",
    "tokenexpirationseconds": 3600,
    "loginendpoint": "auth/login"
  }
}

Flujo de autenticacion:

  1. El cliente hace POST al endpoint de login con sus credenciales.
  2. El controlador valida las credenciales y genera un token JWT.
  3. El cliente almacena el token y lo envia en peticiones posteriores.
  4. El middleware intercepta la peticion, valida el token antes de llegar al controlador.
  5. Si el token es invalido o ha expirado, el middleware devuelve 401 automaticamente.

Controlador de login (genera el token):

PROCEDURE PostLogin(req, res) HELP "POST: auth/login public"
    LOCAL lcUser, lcPass, lcToken, loResp

    lcUser = req.json.username
    lcPass = req.json.password

    IF !THIS.ValidateCredentials(lcUser, lcPass)
        res.status(401).json('{"error":"Invalid credentials"}')
        RETURN
    ENDIF

    * GenerateJWT is a built-in ApiController method
    lcToken = THIS.GenerateJWT(lcUser)

    loResp = THIS.newObject("token,expiresin")
    loResp.token     = lcToken
    loResp.expiresin = 3600
    res.status(200).json(THIS.ToJson(loResp))
ENDPROC

Endpoint protegido (no requiere codigo adicional):

* Omit "public" — JWT middleware runs automatically before this
PROCEDURE GetMyProfile(req, res) HELP "GET: profile"
    LOCAL lcUserId, loResp

    lcUserId = THIS.GetUserIdFromToken(req)

    loResp = THIS.newObject("id,name,email")
    loResp.id    = lcUserId
    loResp.name  = THIS.GetUserName(lcUserId)
    loResp.email = THIS.GetUserEmail(lcUserId)
    res.status(200).json(THIS.ToJson(loResp))
ENDPROC
# Request with valid token:
curl http://localhost:8080/api/profile \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

# Request without token → 401 Unauthorized (automatic)
curl http://localhost:8080/api/profile
# {"error":"Unauthorized","message":"Missing or invalid token"}

CORS — Cross-Origin

El middleware CORS responde automaticamente a las peticiones preflight OPTIONS y agrega los headers necesarios a todas las respuestas.

{
  "allowedorigins": "https://myapp.com,https://admin.myapp.com",
  "allowedmethods": "GET,POST,PUT,DELETE,OPTIONS",
  "allowedheaders": "Content-Type,Authorization,X-Requested-With"
}
# Browser sends preflight:
OPTIONS /api/products HTTP/1.1
Origin: https://myapp.com
Access-Control-Request-Method: POST

# FoxServer responds automatically:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Las peticiones OPTIONS/preflight son respondidas automaticamente por el middleware sin llegar al controlador.

Logging

Registra automaticamente peticiones, respuestas y errores en archivos diarios.

  • Simple — una linea por peticion
  • Detailed — informacion completa incluyendo headers y body (util en desarrollo)
  • JSON — formato estructurado para consumo por herramientas de monitoring

Salida de ejemplo (formato Detailed):

[2026-04-11 14:23:45.123] INFO  GET /api/v1/products?page=1
  Client     : 192.168.1.100
  Status     : 200 OK
  Duration   : 45ms
  Bytes out  : 2048

[2026-04-11 14:23:46.456] ERROR POST /api/v1/orders
  Client     : 192.168.1.100
  Status     : 500 Internal Server Error
  Error      : Database connection failed
  Duration   : 1250ms

Middleware personalizado (Hooks)

Puedes interceptar el ciclo de peticion/respuesta con hooks a nivel de proyecto:

* Root controller — hooks apply to all endpoints in this project
DEFINE CLASS AppController AS ApiController OLEPUBLIC

    * Runs before EVERY endpoint
    FUNCTION BeforeRequest(req, res) AS BOOLEAN
        * Example: block specific IPs
        IF req.remoteIP == "10.0.0.99"
            res.status(403).json('{"error":"Forbidden"}')
            RETURN .F.   && .F. = stop processing
        ENDIF

        * Open shared resources
        SET DELETED ON
        SET EXCLUSIVE OFF

        RETURN .T.   && .T. = continue to controller
    ENDFUNC

    * Runs after EVERY response is sent
    PROCEDURE AfterResponse(req, res)
        * Cleanup, metrics, audit log
        THIS.LogAudit(req.method, req.path, res.statusCode, req.remoteIP)
    ENDPROC

ENDDEFINE
Los hooks son metodos especiales en el controlador raiz. Retornar .F. en BeforeRequest aborta la peticion y devuelve 403 automaticamente.

Siguiente: Licencias →