Routing
Ein http.router gruppiert Endpunkte unter einem URL-Präfix und wendet gemeinsame Middleware an. Jeder http.endpoint definiert einen HTTP-Handler.
Klassifikation: Routing-Referenz. Konfigurationsblöcke sind Registry-Teilfragmente, sofern sie nicht einen Namespace und jeden referenzierten Eintrag enthalten. Handler-Blöcke verwenden Funktions-IDs der Anwendung, anstatt eine Datenschicht zu definieren.
Architektur
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]
Einträge referenzieren Eltern über Metadaten:
- Router:
meta.server: app:gateway - Endpunkte:
meta.router: app:api
Router-Konfiguration
- name: api
kind: http.router
meta:
server: gateway
prefix: /api/v1
middleware:
- cors
- compress
options:
cors.allow.origins: "*"
post_middleware:
- endpoint_firewall
| Feld | Typ | Beschreibung |
|---|---|---|
meta.server |
Registry-ID | Übergeordneter HTTP-Server |
prefix |
string | URL-Präfix für alle Routen |
middleware |
[]string | Pre-Match-Middleware |
options |
map | Middleware-Optionen |
post_middleware |
[]string | Post-Match-Middleware |
post_options |
map | Post-Match-Middleware-Optionen |
Endpunkt-Konfiguration
- name: get_user
kind: http.endpoint
meta:
router: api
method: GET
path: /users/{id}
func: app.users:get_user
| Feld | Typ | Beschreibung |
|---|---|---|
meta.router |
Registry-ID | Übergeordneter Router |
method |
string | HTTP-Methode: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, TRACE oder * für jede Methode |
path |
string | URL-Pfadmuster (beginnt mit /) |
func |
Registry-ID | Handler-Funktion |
Pfadparameter
Verwenden Sie {param}-Syntax für URL-Parameter:
- name: get_post
kind: http.endpoint
meta:
router: api
method: GET
path: /users/{user_id}/posts/{post_id}
func: get_user_post
Zugriff im 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
Wildcard-Pfade
Verbleibende Pfadsegmente mit {param...} erfassen:
- name: serve_files
kind: http.endpoint
meta:
router: api
method: GET
path: /files/{filepath...}
func: serve_file
Der Wildcard passt auf die verbleibenden Segmente, sodass eine Anfrage wie GET /api/v1/files/docs/guides/readme.md an den Handler weitergereicht wird. Der erfasste Rest wird mit req:param unter dem Namen ohne die abschließenden Punkte gelesen:
local filepath = req:param("filepath") -- "docs/guides/readme.md"
Der Wildcard muss das letzte Segment im Pfad sein.
Routen-Vorrang
Alle Router registrieren ihre Endpunkte in einer einzigen Mustermenge, mit dem prefix des Routers vorangestellt, und Gos ServeMux entscheidet, welches Muster eine Anfrage bedient. Seine Regeln gelten unverändert:
- Das spezifischste Muster gewinnt. Ein Muster ist spezifischer als ein anderes, wenn es eine echte Teilmenge der Anfragen dieses Musters trifft, sodass
/users/admingegenüber/users/{id}gewinnt und/files/{name}gegenüber/files/{path...}. - Ein Muster mit Methode ist spezifischer als derselbe Pfad ohne Methode, sodass ein
GET-Endpunkt beiGET-Anfragen Vorrang vor einem*-Endpunkt auf demselben Pfad hat. - Ein abschließendes
{path...}oder/trifft einen ganzen Teilbaum und verliert gegen jedes Muster, das eine Teilmenge davon trifft. - Der Abgleich erfolgt auf dem bereinigten, dekodierten Pfad; die Spezifität hängt nie von der Registrierungsreihenfolge ab.
Zwei Muster können auch unmittelbar in Konflikt geraten: Keines ist spezifischer als das andere, doch sie überschneiden sich, wie bei /users/{id}/settings und /users/admin/{section}. Das ist ein Konfigurationsfehler. Der Router meldet ihn beim Neuaufbau, der Neuaufbau schlägt fehl, und die vorherige Routenmenge bleibt im Betrieb.
Handler-Funktionen
Endpunkt-Handler verwenden das Modul http, um auf Request- und Response-Objekte zuzugreifen. Die API-Referenz finden Sie unter HTTP-Modul.
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 }
Middleware-Optionen
Middleware-Optionen verwenden Punkt-Notation mit dem Middleware-Namen als Präfix:
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"
Post-Match-Middleware verwendet post_options:
post_middleware:
- endpoint_firewall
post_options:
endpoint_firewall.action: "access"
Pre-Handler- und Post-Match-Middleware
Pre-Handler (middleware) läuft, nachdem der Server eine Route ausgewählt hat, aber bevor Routenparameter und Endpunktmetadaten an den Request-Kontext angefügt werden:
- CORS (behandelt OPTIONS-Preflight)
- Komprimierung
- Rate-Limiting
- Real-IP-Erkennung
- Token-Authentifizierung (Kontext-Anreicherung)
Post-Match (post_middleware) läuft, nachdem Routenparameter und Endpunktmetadaten angefügt wurden:
- Endpoint-Firewall (benötigt Routen-Info für Autorisierung)
- Ressourcen-Firewall
- 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
endpoint_firewall gehört in die Post-Match-Kette, weil sie die ID des abgeglichenen Endpunkts benötigt. Bei nicht abgeglichenen Anfragen läuft keine der beiden Router-Ketten.
Router- und Endpunktverdrahtung
Dieses Beispiel definiert den Handler-Eintrag für die Liste. Die Funktions-IDs app:get_user_by_id und app:create_user verweisen auf Handler, die an anderer Stelle im selben Namespace definiert sind.
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
Geschützte Routen
Die folgende Konfiguration trennt öffentliche Routen von Routen, die Authentifizierung und Autorisierung erfordern:
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
Siehe auch
- Server – HTTP-Server-Konfiguration
- Statische Dateien – Bereitstellung statischer Dateien
- Middleware – Verfügbare Middleware
- HTTP-Modul – Lua-HTTP-API