Roteamento

Uma entrada http.router agrupa endpoints sob um prefixo de URL e aplica middleware compartilhado. Cada http.endpoint define um handler HTTP.

Classificação: referência de roteamento. Os blocos de configuração são fragmentos parciais do registro, a menos que incluam um namespace e todas as entradas referenciadas. Os blocos de handlers usam IDs de funções pertencentes à aplicação em vez de definir uma camada de dados.

Arquitetura

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]

As entradas referenciam seus pais por metadados:

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

Configuração do roteador

- name: api
  kind: http.router
  meta:
    server: gateway
  prefix: /api/v1
  middleware:
    - cors
    - compress
  options:
    cors.allow.origins: "*"
  post_middleware:
    - endpoint_firewall
Campo Tipo Descrição
meta.server ID do registro Servidor HTTP pai
prefix string Prefixo de URL para todas as rotas
middleware []string Middleware de pré-handler
options map Opções do middleware
post_middleware []string Middleware de pós-match
post_options map Opções do middleware de pós-match

Configuração do endpoint

- name: get_user
  kind: http.endpoint
  meta:
    router: api
  method: GET
  path: /users/{id}
  func: app.users:get_user
Campo Tipo Descrição
meta.router ID do Registro Roteador pai
method string Método HTTP: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, TRACE, ou * para qualquer método
path string Padrão de caminho URL (começa com /)
func ID do Registro Função handler

Parâmetros de caminho

Use a sintaxe {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

Acesse-os no 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

Caminhos curinga

Capture os segmentos de caminho restantes com {param...}:

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

O curinga corresponde aos segmentos restantes, de modo que uma requisição como GET /api/v1/files/docs/guides/readme.md é despachada para o handler. A cauda capturada é lida com req:param sob o nome sem os pontos finais:

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

O curinga deve ser o último segmento do caminho.

Precedência de Rotas

Todos os roteadores registram seus endpoints em um único conjunto de padrões, prefixado pelo prefix do roteador, e o ServeMux do Go decide qual padrão atende uma requisição. Suas regras se aplicam sem alteração:

  • O padrão mais específico vence. Um padrão é mais específico que outro quando corresponde a um subconjunto estrito das requisições daquele padrão, então /users/admin vence /users/{id}, e /files/{name} vence /files/{path...}.
  • Um padrão com método é mais específico que o mesmo caminho sem método, então um endpoint GET tem precedência sobre um endpoint * no mesmo caminho para requisições GET.
  • Um {path...} ou / final corresponde a uma subárvore inteira e perde para qualquer padrão que corresponda a um subconjunto dela.
  • A correspondência é feita sobre o caminho limpo e decodificado; a especificidade nunca depende da ordem de registro.

Dois padrões também podem entrar em conflito direto: nenhum é mais específico que o outro, mas eles se sobrepõem, como em /users/{id}/settings e /users/admin/{section}. Isso é um erro de configuração. O roteador o expõe ao reconstruir, a reconstrução falha, e o conjunto de rotas anterior permanece em serviço.

Funções Handler

Os handlers de endpoint usam o módulo http para acessar os objetos de requisição e resposta. Consulte o módulo HTTP para ver a referência da API.

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 }

Opções de middleware

As opções de middleware usam notação de ponto, com o nome do middleware como prefixo:

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"

O middleware de pós-match usa post_options:

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

Middleware de pré-handler e pós-match

O middleware de pré-handler (middleware) é executado depois que o servidor seleciona uma rota, mas antes de os parâmetros da rota e os metadados do endpoint serem anexados ao contexto da requisição:

  • CORS, incluindo preflight OPTIONS
  • Compressão
  • Rate limiting
  • Detecção de IP real
  • Autenticação por token, que enriquece o contexto

O middleware de pós-match (post_middleware) é executado depois que os parâmetros da rota e os metadados do endpoint são anexados:

  • Firewall de endpoint, que precisa das informações da rota para autorizar
  • Firewall de recurso
  • Relay WebSocket
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
A autenticação por token pertence à cadeia de pré-handler porque enriquece o contexto da requisição antes da autorização. Middleware de autorização como endpoint_firewall pertence à cadeia de pós-match porque precisa do ID do endpoint correspondente. Requisições sem correspondência não executam nenhuma das cadeias do roteador.

Ligação entre roteador e endpoint

Este exemplo define a entrada do handler de listagem. Os IDs de função app:get_user_by_id e app:create_user referenciam handlers definidos em outro local do mesmo 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

Rotas protegidas

A configuração a seguir separa as rotas públicas das que exigem autenticação e autorização:

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

Veja também