HTTP
Das Modul http liest die aktuelle serverseitige Anfrage und erstellt ihre Response einschließlich Headern, Routendaten, Body-Inhalt, Streaming-Ausgabe und Server-Sent Events.
Diese Seite ist eine API-Referenz mit Teilrezepten für Handler. Namen wie id, data, token und Anwendungs-Callbacks stammen aus dem umgebenden Handler. Request-Accessors liefern im Allgemeinen value, error, Response-Mutationen error; Beispiele, die ein Ergebnis verwenden, prüfen diese Fehler.
Zur Serverkonfiguration siehe HTTP-Server.
Laden
local http = require("http")
Auf die Anfrage zugreifen
Holen Sie den aktuellen HTTP-Anfrage-Kontext:
local req, err = http.request({
timeout = 5000, -- 5 second body read timeout
max_body = 10485760 -- 10MB max body
})
if err then return nil, err end
| Parameter | Typ | Beschreibung |
|---|---|---|
options.timeout |
integer | Body-Lese-Timeout in ms (Standard: 300000 / 5 min) |
options.max_body |
integer | Max. Body-Größe in Bytes (Standard: 120MB) |
Gibt zurück: Request, error
Auf die Response zugreifen
Holen Sie den aktuellen HTTP-Response-Kontext:
local res, err = http.response()
if err then return nil, err end
Gibt zurück: Response, error
Request-Methoden
method
local method, method_err = req:method()
if method_err then return nil, method_err end
if method == http.METHOD.GET then
return get_resource(id)
elseif method == http.METHOD.POST then
local data, body_err = req:body_json()
if body_err then return nil, body_err end
return create_resource(data)
elseif method == http.METHOD.PUT then
local data, body_err = req:body_json()
if body_err then return nil, body_err end
return update_resource(id, data)
elseif method == http.METHOD.DELETE then
return delete_resource(id)
end
path
local path, err = req:path()
if err then return nil, err end
print(path) -- "/api/users/123"
-- Route based on path
if path:match("^/api/") then
return handle_api(req)
end
query
Holt einen einzelnen Query-Parameter.
-- GET /search?q=hello&page=2&limit=10
local query, query_err = req:query("q")
if query_err then return nil, query_err end
-- With defaults
local page_text, page_err = req:query("page")
if page_err then return nil, page_err end
local page = tonumber(page_text) or 1
query_params
Holt alle Query-Parameter. Mehrere Werte für denselben Schlüssel werden mit Kommas verbunden.
-- GET /search?tags=lua&tags=go&active=true
local params, err = req:query_params()
if err then return nil, err end
-- {tags = "lua,go", active = "true"}
for key, value in pairs(params) do
print(key .. ": " .. value)
end
header
local uuid = require("uuid")
local auth, auth_err = req:header("Authorization")
if auth_err then return nil, auth_err end
if not auth then
local type_err = res:set_content_type(http.CONTENT.JSON)
if type_err then return nil, type_err end
local status_err = res:set_status(http.STATUS.UNAUTHORIZED)
if status_err then return nil, status_err end
return res:write_json({error = "Missing authorization"})
end
local correlation_id, correlation_err = req:header("X-Correlation-ID")
if correlation_err then return nil, correlation_err end
if not correlation_id then
correlation_id, correlation_err = uuid.v4()
if correlation_err then return nil, correlation_err end
end
Die Suche ist nicht case-sensitiv: req:header("content-type") und req:header("Content-Type") liefern denselben Wert. Ein mehrfach gesendeter Header liefert seine Werte mit ", " verbunden. Ein nicht vorhandener Header liefert nil.
headers
Ruft alle Request-Header ab.
local headers, err = req:headers()
for name, value in pairs(headers) do
print(name .. ": " .. value)
end
Gibt zurück: table, error
Die Schlüssel sind kanonische Header-Namen (Content-Type, X-Correlation-ID), unabhängig von der Schreibweise, die der Client gesendet hat. Wiederholte Header werden mit ", " verbunden, wie bei req:header().
content_type
Holt den Content-Type-Header.
local ct, type_err = req:content_type() -- "application/json; charset=utf-8" or nil
if type_err then return nil, type_err end
content_length
Holt den Content-Length-Header-Wert.
local length, length_err = req:content_length() -- number of bytes
if length_err then return nil, length_err end
host
Holt den Host-Header.
local host, host_err = req:host() -- "example.com:8080"
if host_err then return nil, host_err end
param
Holt URL-Route-Parameter (aus Pfadmustern wie /users/:id).
-- Route: /users/:id/posts/:post_id
local id, param_err = req:param("id")
if param_err then return nil, param_err end
local valid = false
if id then
local validate_err
valid, validate_err = uuid.validate(id)
if validate_err then return nil, validate_err end
end
if not valid then
local type_err = res:set_content_type(http.CONTENT.JSON)
if type_err then return nil, type_err end
local status_err = res:set_status(http.STATUS.BAD_REQUEST)
if status_err then return nil, status_err end
return res:write_json({error = "Invalid ID format"})
end
params
Holt alle Route-Parameter.
-- Route: /orgs/:org/repos/:repo/issues/:issue
local p, err = req:params()
if err then return nil, err end
-- {org = "acme", repo = "widget", issue = "123"}
local issue = get_issue(p.org, p.repo, p.issue)
body
Liest den vollständigen Anfrage-Body als String.
local body, err = req:body()
if err then return nil, err end
-- Parse XML manually
local is_xml, type_err = req:is_content_type("application/xml")
if type_err then return nil, type_err end
if is_xml then
local data = parse_xml(body)
end
-- Avoid logging raw request bodies; record only non-sensitive metadata.
logger.debug("Request body read", {length = #body})
body_json
Liest und parst Body als JSON.
local data, err = req:body_json()
if err then
local type_err = res:set_content_type(http.CONTENT.JSON)
if type_err then return nil, type_err end
local status_err = res:set_status(http.STATUS.BAD_REQUEST)
if status_err then return nil, status_err end
return res:write_json({error = "Invalid JSON: " .. err:message()})
end
-- Validate required fields
if not data.name or not data.email then
local type_err = res:set_content_type(http.CONTENT.JSON)
if type_err then return nil, type_err end
local status_err = res:set_status(http.STATUS.BAD_REQUEST)
if status_err then return nil, status_err end
return res:write_json({error = "Missing required fields"})
end
local user = create_user(data)
has_body
local has_body, body_state_err = req:has_body()
if body_state_err then return nil, body_state_err end
if has_body then
local data, body_err = req:body_json()
if body_err then return nil, body_err end
process(data)
else
local type_err = res:set_content_type(http.CONTENT.JSON)
if type_err then return nil, type_err end
local status_err = res:set_status(http.STATUS.BAD_REQUEST)
if status_err then return nil, status_err end
return res:write_json({error = "Request body required"})
end
is_content_type
local is_json, type_check_err = req:is_content_type("application/json")
if type_check_err then return nil, type_check_err end
if not is_json then
local type_err = res:set_content_type(http.CONTENT.JSON)
if type_err then return nil, type_err end
local status_err = res:set_status(415)
if status_err then return nil, status_err end
return res:write_json({error = "Content-Type must be application/json"})
end
accepts
local accepts_json, json_accept_err = req:accepts("application/json")
if json_accept_err then return nil, json_accept_err end
local accepts_html, html_accept_err = req:accepts("text/html")
if html_accept_err then return nil, html_accept_err end
if accepts_json then
return res:write_json(data)
elseif accepts_html then
local type_err = res:set_content_type("text/html; charset=utf-8")
if type_err then return nil, type_err end
return res:write(render_html(data))
else
local type_err = res:set_content_type(http.CONTENT.JSON)
if type_err then return nil, type_err end
local status_err = res:set_status(http.STATUS.NOT_ACCEPTABLE)
if status_err then return nil, status_err end
return res:write_json({error = "Cannot produce acceptable response"})
end
remote_addr
local addr, addr_err = req:remote_addr() -- "192.168.1.100:54321"
if addr_err then return nil, addr_err end
-- Extract the host from IPv4 and bracketed IPv6 addresses
local ip = addr:match("^%[([^%]]+)%]:%d+$")
or addr:match("^([^:]+):%d+$")
or addr
-- Rate limiting by IP
if rate_limiter:is_limited(ip) then
local type_err = res:set_content_type(http.CONTENT.JSON)
if type_err then return nil, type_err end
local status_err = res:set_status(http.STATUS.TOO_MANY_REQUESTS)
if status_err then return nil, status_err end
return res:write_json({error = "Too many requests"})
end
parse_multipart
Parst Multipart-Formulardaten einschließlich Datei-Uploads. Das optionale Argument max_memory bestimmt, wie viele Bytes im Speicher bleiben, bevor Daten in temporäre Dateien ausgelagert werden; Standard sind 32 MB.
local uuid = require("uuid")
local form, err = req:parse_multipart() -- or req:parse_multipart(8 * 1024 * 1024)
if err then
local type_err = res:set_content_type(http.CONTENT.JSON)
if type_err then return nil, type_err end
local status_err = res:set_status(http.STATUS.BAD_REQUEST)
if status_err then return nil, status_err end
return res:write_json({error = "Invalid form data"})
end
-- Access form values
local title = form.values.title
local description = form.values.description
-- Access uploaded files
if form.files.avatar then
local file = form.files.avatar[1]
local filename, name_err = file:name() -- untrusted client metadata
if name_err then return nil, name_err end
local size, size_err = file:size()
if size_err then return nil, size_err end
local content_type, header_err = file:header("Content-Type") -- "image/jpeg"
if header_err then return nil, header_err end
-- Dateiinhalt lesen
local stream = file:stream()
local parts = {}
while true do
local chunk, err = stream:read(65536)
if err or not chunk then break end
parts[#parts + 1] = chunk
end
stream:close()
local content = table.concat(parts)
local stream, stream_err = file:stream()
if stream_err then return nil, stream_err end
local stored_name, id_err = uuid.v7()
if id_err then
stream:close()
return nil, id_err
end
local _, write_err = uploads:writefile(stored_name, stream, "wx")
local _, close_err = stream:close()
if write_err then return nil, write_err end
if close_err then return nil, close_err end
end
-- Handle multiple files
if form.files.documents then
for _, file in ipairs(form.files.documents) do
process_document(file)
end
end
stream
Holt Anfrage-Body als Stream für große Dateien.
local stream, stream_err = req:stream()
if stream_err then return nil, stream_err end
-- Process in chunks
local read_err
while true do
local chunk
chunk, read_err = stream:read(65536) -- 64KB chunks
if read_err or not chunk then break end
process_chunk(chunk)
end
local _, close_err = stream:close()
if read_err then return nil, read_err end
if close_err then return nil, close_err end
Response-Methoden
set_status
local status_err = res:set_status(http.STATUS.CREATED)
if status_err then return nil, status_err end
-- Other common choices: 204 No Content, 400 Bad Request,
-- 401 Unauthorized, 403 Forbidden, 404 Not Found, and 500 Internal Error.
set_header
local request_id_err = res:set_header("X-Request-ID", correlation_id)
if request_id_err then return nil, request_id_err end
local cache_err = res:set_header("Cache-Control", "max-age=3600")
if cache_err then return nil, cache_err end
local rate_err = res:set_header("X-RateLimit-Remaining", tostring(remaining))
if rate_err then return nil, rate_err end
-- CORS headers
local origin_err = res:set_header("Access-Control-Allow-Origin", "*")
if origin_err then return nil, origin_err end
local methods_err = res:set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
if methods_err then return nil, methods_err end
local headers_err = res:set_header("Access-Control-Allow-Headers", "Content-Type, Authorization")
if headers_err then return nil, headers_err end
set_content_type
local type_err = res:set_content_type(http.CONTENT.JSON)
if type_err then return nil, type_err end
-- Other examples: "text/html; charset=utf-8" or "application/pdf".
write
Schreibt in den Response-Body.
local write_err = res:write("Hello, World!")
if write_err then return nil, write_err end
-- Build response incrementally
for _, fragment in ipairs({
"<html><body>",
"<h1>Title</h1>",
"<p>Content</p>",
"</body></html>"
}) do
local fragment_err = res:write(fragment)
if fragment_err then return nil, fragment_err end
end
write_json
Kodiert Wert als JSON und schreibt ihn.
-- Success response
local write_err = res:write_json({
data = users,
total = count,
page = page
})
if write_err then return nil, write_err end
-- Error response
local type_err = res:set_content_type(http.CONTENT.JSON)
if type_err then return nil, type_err end
local status_err = res:set_status(http.STATUS.BAD_REQUEST)
if status_err then return nil, status_err end
local error_write_err = res:write_json({
error = "Validation failed",
details = {
{field = "email", message = "Invalid format"},
{field = "age", message = "Must be positive"}
}
})
if error_write_err then return nil, error_write_err end
flush
Gepufferte Daten an Client senden.
set_transfer
Setzt Transfer-Kodierung für Streaming.
-- Chunked transfer
local transfer_err = res:set_transfer(http.TRANSFER.CHUNKED)
if transfer_err then return nil, transfer_err end
for chunk in get_chunks() do
local write_err = res:write(chunk)
if write_err then return nil, write_err end
local flush_err = res:flush()
if flush_err then return nil, flush_err end
end
-- Server-Sent Events
local sse_err = res:set_transfer(http.TRANSFER.SSE)
if sse_err then return nil, sse_err end
write_event
Schreibt ein Server-Sent Event.
-- Real-time updates
local transfer_err = res:set_transfer(http.TRANSFER.SSE)
if transfer_err then return nil, transfer_err end
local connected_err = res:write_event({name = "connected", data = {client_id = client_id}})
if connected_err then return nil, connected_err end
for progress in task:progress() do
local event_err = res:write_event({name = "progress", data = {percent = progress}})
if event_err then return nil, event_err end
end
local complete_err = res:write_event({name = "complete", data = {result = result}})
if complete_err then return nil, complete_err end
-- Chat messages
local message_err = res:write_event({name = "message", data = {
from = "alice",
text = "Hello!",
timestamp = time.now():unix()
}})
if message_err then return nil, message_err end
Konstanten
HTTP-Methoden
http.METHOD.GET
http.METHOD.POST
http.METHOD.PUT
http.METHOD.DELETE
http.METHOD.PATCH
http.METHOD.HEAD
http.METHOD.OPTIONS
Statuscodes
-- Success (2xx)
http.STATUS.OK -- 200
http.STATUS.CREATED -- 201
http.STATUS.ACCEPTED -- 202
http.STATUS.NO_CONTENT -- 204
http.STATUS.PARTIAL_CONTENT -- 206
-- Redirect (3xx)
http.STATUS.MOVED_PERMANENTLY -- 301
http.STATUS.FOUND -- 302
http.STATUS.SEE_OTHER -- 303
http.STATUS.NOT_MODIFIED -- 304
http.STATUS.TEMPORARY_REDIRECT -- 307
http.STATUS.PERMANENT_REDIRECT -- 308
-- Client Error (4xx)
http.STATUS.BAD_REQUEST -- 400
http.STATUS.UNAUTHORIZED -- 401
http.STATUS.PAYMENT_REQUIRED -- 402
http.STATUS.FORBIDDEN -- 403
http.STATUS.NOT_FOUND -- 404
http.STATUS.METHOD_NOT_ALLOWED -- 405
http.STATUS.NOT_ACCEPTABLE -- 406
http.STATUS.CONFLICT -- 409
http.STATUS.GONE -- 410
http.STATUS.UNPROCESSABLE -- 422
http.STATUS.TOO_MANY_REQUESTS -- 429
-- Server Error (5xx)
http.STATUS.INTERNAL_ERROR -- 500 (alias: INTERNAL_SERVER_ERROR)
http.STATUS.NOT_IMPLEMENTED -- 501
http.STATUS.BAD_GATEWAY -- 502
http.STATUS.SERVICE_UNAVAILABLE -- 503
http.STATUS.GATEWAY_TIMEOUT -- 504
http.STATUS.VERSION_NOT_SUPPORTED -- 505
Content-Types
http.CONTENT.JSON -- "application/json"
http.CONTENT.FORM -- "application/x-www-form-urlencoded"
http.CONTENT.MULTIPART -- "multipart/form-data"
http.CONTENT.TEXT -- "text/plain"
http.CONTENT.STREAM -- "application/octet-stream"
Transfer-Modi
http.TRANSFER.CHUNKED -- "chunked"
http.TRANSFER.SSE -- "sse"
Ältere Fehlertyp-Konstanten
Das Modul exportiert diese Kompatibilitätszeichenketten, aktuelle Request- und Response-Methoden geben sie jedoch nicht zurück. Runtime-Fehler verwenden die unten beschriebenen strukturierten Arten errors.*.
http.ERROR.PARSE_FAILED -- Form/multipart parse error
http.ERROR.INVALID_STATE -- Invalid response state
http.ERROR.WRITE_FAILED -- Response write error
http.ERROR.STREAM_ERROR -- Body stream error
Fehler
| Bedingung | Art | Wiederholbar |
|---|---|---|
| Kein HTTP-Kontext | errors.INTERNAL |
nein |
| Body zu groß | errors.INVALID |
nein |
| Lese-Timeout | errors.INTERNAL |
nein |
| Ungültiges JSON | errors.INVALID |
nein |
| Nicht Multipart | errors.INVALID |
nein |
| Header bereits gesendet | errors.INVALID |
nein |
| Schreiben fehlgeschlagen | errors.INTERNAL |
nein |
Siehe Fehlerbehandlung für die Arbeit mit Fehlern.
Fügen Sie http zur Liste modules: des ausführbaren Eintrags hinzu, bevor Sie es per require laden. Beispiele mit uuid, fs oder time erfordern diese Module separat.
body(), body_json(), stream() und parse_multipart() verbrauchen denselben Request-Body. Wählen Sie pro Handler genau einen Lesepfad. body() und body_json() wenden Timeout und Größenlimit des Request-Objekts an; stream() liest inkrementell und verwendet diese beiden Optionen nicht.
has_body() liefert nur dann true, wenn ein Request-Body und eine positive Content-Length vorhanden sind. Bei Chunked Requests oder unbekannter Länge kann es false liefern. Handler, die solche Bodies erlauben, sollten ihren gewählten Body-Reader aufrufen und dessen Fehler behandeln.
Der Helper accepts() dieser Version führt exakte, kommaseparierte Abgleiche und */* aus. Er verarbeitet weder Medientypparameter noch Subtyp-Wildcards oder Qualitätsgewichte; fehlt der Header Accept, liefert er false. Verwenden Sie anwendungseigene Content Negotiation, wenn diese HTTP-Semantik relevant ist.
Multipart-Feldwerte sind Zeichenketten, wenn ein Feld einmal vorkommt, und Arrays bei Wiederholungen. Behandeln Sie hochgeladene Dateinamen und Content-Type als nicht vertrauenswürdige Metadaten. Erzeugen Sie den Speichernamen selbst und untersuchen Sie den Inhalt unabhängig, wenn dessen Typ relevant ist.
Der exklusive Schreibmodus wx verhindert das Überschreiben eines vorhandenen Objekts. Ein fehlgeschlagener Schreibvorgang beweist nicht, dass das Ziel dieser Anfrage gehört; entfernen Sie es im Fehlerpfad daher nicht blind. Anwendungen mit Cleanup-Anforderung sollten Uploads unter einem eigentumsverfolgten temporären Namen ablegen und erst nach erfolgreichem Schreiben übernehmen.
set_status() schreibt den Status und committet die Response-Header sofort. Rufen Sie set_header(), set_content_type() oder set_transfer() vorher auf; spätere Header-Änderungen liefern errors.INVALID.
write(), write_json(), flush() und write_event() committen ebenfalls die Header. write_json() setzt Content-Type: application/json nur, wenn die Header noch nicht committet wurden.