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/adminvence/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
GETtem precedência sobre um endpoint*no mesmo caminho para requisiçõesGET. - 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
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
- Servidor - Configuração do servidor HTTP
- Arquivos estáticos - Serviço de arquivos estáticos
- Middleware - Middleware disponível
- Módulo HTTP - API HTTP para Lua