Routing

Un http.router agrupa endpoints bajo un prefijo de URL y aplica middleware compartido. Cada http.endpoint define un handler HTTP.

Clasificación: referencia de enrutamiento. Los bloques de configuración son fragmentos parciales de registro salvo que incluyan un namespace y cada entrada referenciada. Los bloques de handler usan ID de funciones propiedad de la aplicación en lugar de definir una capa de datos.

Arquitectura

flowchart TB
    S[http.service
:8080] --> R1[http.router
/api] S --> R2[http.router
/admin] S --> ST[http.static
/] R1 --> E1[GET /users] R1 --> E2[POST /users] R1 --> E3["GET /users/{id}"] R2 --> E4[GET /stats] R2 --> E5[POST /config]

Las entradas referencian padres vía metadatos:

  • Routers: meta.server: app:gateway
  • Endpoints: meta.router: app:api

Configuración del Router

- name: api
  kind: http.router
  meta:
    server: gateway
  prefix: /api/v1
  middleware:
    - cors
    - compress
  options:
    cors.allow.origins: "*"
  post_middleware:
    - endpoint_firewall
Campo Tipo Descripción
meta.server ID de Registro Servidor HTTP padre
prefix string Prefijo de URL para todas las rutas
middleware []string Middleware pre-match
options map Opciones de middleware
post_middleware []string Middleware post-match
post_options map Opciones de middleware post-match

Configuración de Endpoint

- name: get_user
  kind: http.endpoint
  meta:
    router: api
  method: GET
  path: /users/{id}
  func: app.users:get_user
Campo Tipo Descripción
meta.router ID de Registro Router padre
method string Método HTTP: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, TRACE, o * para cualquier método
path string Patrón de ruta URL (comienza con /)
func ID de Registro Función handler

Parámetros de Ruta

Use sintaxis {param} para parámetros de URL:

- name: get_post
  kind: http.endpoint
  meta:
    router: api
  method: GET
  path: /users/{user_id}/posts/{post_id}
  func: get_user_post

Acceso en handler:

local http = require("http")

local function handler()
    local req, req_err = http.request()
    if req_err then return nil, req_err end
    local user_id, user_err = req:param("user_id")
    if user_err then return nil, user_err end
    local post_id, post_err = req:param("post_id")
    if post_err then return nil, post_err end

    return {user_id = user_id, post_id = post_id}
end

Rutas Comodín

Capture segmentos de ruta restantes con {param...}:

- name: serve_files
  kind: http.endpoint
  meta:
    router: api
  method: GET
  path: /files/{filepath...}
  func: serve_file

El comodín coincide con los segmentos restantes, por lo que una solicitud como GET /api/v1/files/docs/guides/readme.md se despacha al handler. La cola capturada se lee con req:param bajo el nombre sin los puntos finales:

local filepath = req:param("filepath")  -- "docs/guides/readme.md"

El comodín debe ser el último segmento de la ruta.

Precedencia de Rutas

Todos los routers registran sus endpoints en un único conjunto de patrones, prefijados por el prefix del router, y el ServeMux de Go decide qué patrón sirve una solicitud. Sus reglas se aplican sin cambios:

  • Gana el patrón más específico. Un patrón es más específico que otro cuando coincide con un subconjunto estricto de las solicitudes de ese patrón, por lo que /users/admin gana a /users/{id}, y /files/{name} gana a /files/{path...}.
  • Un patrón con método es más específico que la misma ruta sin él, por lo que un endpoint GET tiene precedencia sobre un endpoint * en la misma ruta para las solicitudes GET.
  • Un {path...} o / final coincide con un subárbol completo y pierde ante cualquier patrón que coincida con un subconjunto de él.
  • La coincidencia se hace sobre la ruta limpiada y decodificada; la especificidad nunca depende del orden de registro.

Dos patrones también pueden entrar en conflicto directo: ninguno es más específico que el otro, pero se solapan, como ocurre con /users/{id}/settings y /users/admin/{section}. Esto es un error de configuración. El router lo expone cuando reconstruye, la reconstrucción falla, y el conjunto de rutas anterior permanece en servicio.

Funciones Handler

Los handlers de endpoint usan el módulo http para acceder a objetos de solicitud y respuesta. Consulta Módulo HTTP para la referencia de las API de solicitud y respuesta.

local http = require("http")

local function handler()
    local req, req_err = http.request()
    if req_err then return nil, req_err end
    local res, res_err = http.response()
    if res_err then return nil, res_err end

    local user_id, param_err = req:param("id")
    if param_err then return nil, param_err end
    local user, call_err = funcs.call("app.users:get_user", user_id)
    if call_err then return nil, call_err end

    res:set_status(http.STATUS.OK)
    res:write_json(user)
end

return { handler = handler }

Opciones de Middleware

Las opciones de middleware usan notación de punto con el nombre del middleware como prefijo:

middleware:
  - cors
  - ratelimit
  - token_auth
options:
  cors.allow.origins: "https://app.example.com"
  cors.allow.methods: "GET,POST,PUT,DELETE"
  ratelimit.requests: "100"
  ratelimit.window: "1m"
  token_auth.store: "app:tokens"
  token_auth.header.name: "Authorization"

El middleware post-match usa post_options:

post_middleware:
  - endpoint_firewall
post_options:
  endpoint_firewall.action: "access"

Middleware pre-handler y post-match

Pre-handler (middleware) se ejecuta después de que el servidor selecciona una ruta, pero antes de adjuntar al contexto de la solicitud los parámetros de ruta y los metadatos del endpoint:

  • CORS (maneja preflight OPTIONS)
  • Compresión
  • Rate limiting
  • Detección de IP real
  • Autenticación de token (enriquecimiento de contexto)

Post-match (post_middleware) se ejecuta después de adjuntar los parámetros de ruta y los metadatos del endpoint:

  • Firewall de endpoint (necesita info de ruta para autorización)
  • Firewall de recurso
  • WebSocket relay
middleware:        # Before endpoint metadata: matched routes only
  - cors
  - compress
  - token_auth     # Enriches context with actor/scope

post_middleware:   # Post-match: matched routes only
  - endpoint_firewall  # Uses actor from token_auth
La autenticación mediante token pertenece a la cadena pre-handler porque enriquece el contexto de la solicitud antes de la autorización. El middleware de autorización, como endpoint_firewall, pertenece a la cadena post-match porque necesita el ID del endpoint coincidente. Las solicitudes sin coincidencia no ejecutan ninguna de las dos cadenas del router.

Conexión del router y los endpoints

Este ejemplo define la entrada del handler de lista. Los ID de función app:get_user_by_id y app:create_user hacen referencia a handlers definidos en otro lugar del mismo namespace.

version: "1.0"
namespace: app

entries:
  # Server
  - name: gateway
    kind: http.service
    addr: ":8080"
    lifecycle:
      auto_start: true

  # API Router
  - name: api
    kind: http.router
    meta:
      server: gateway
    prefix: /api/v1
    middleware:
      - cors
      - compress
      - ratelimit
    options:
      cors.allow.origins: "https://app.example.com"
      ratelimit.requests: "100"
      ratelimit.window: "1m"

  # Handler function
  - name: get_users
    kind: function.lua
    source: file://handlers/users.lua
    method: list
    modules:
      - http
      - json
      - sql

  # Endpoints
  - name: list_users
    kind: http.endpoint
    meta:
      router: api
    method: GET
    path: /users
    func: get_users

  - name: get_user
    kind: http.endpoint
    meta:
      router: api
    method: GET
    path: /users/{id}
    func: app:get_user_by_id

  - name: create_user
    kind: http.endpoint
    meta:
      router: api
    method: POST
    path: /users
    func: app:create_user

Rutas Protegidas

La configuración siguiente separa las rutas públicas de las que requieren autenticación y autorización:

entries:
  # Public routes (no auth)
  - name: public
    kind: http.router
    meta:
      server: gateway
    prefix: /api/public
    middleware:
      - cors

  # Protected routes
  - name: protected
    kind: http.router
    meta:
      server: gateway
    prefix: /api
    middleware:
      - cors
      - token_auth
    options:
      token_auth.store: app:tokens
    post_middleware:
      - endpoint_firewall

Véase también