std.web
A router over http.serve.
import std.http
import std.web
routes = [
{ method: "GET", path: "/", handler: fn(req) http.html(home()) },
{ method: "GET", path: "/posts/:id", handler: fn(req) show(req.params.id) },
{ method: "POST", path: "/posts", handler: create },
web.static("/assets", "public/assets"),
]
http.serve(8080, web.router(routes, middleware))
Path parameters
:name segments arrive in req.params:
fn show(req) {
id = req.params.id
http.json(find(id))
}
GET routes answer HEAD
A GET route responds to HEAD automatically, with the headers and no body.
This exists because it is what every client expects - health checks, link checkers, proxies and curl -I all send HEAD - and a router that 404s them looks broken while working perfectly for browsers.
Middleware
A middleware is fn(req, next). It can inspect the request, call next(req), and modify the response:
fn timing(req, next) {
start = time.monotonic()
resp = next(req)
log.info("request", { path: req.path, ms: time.monotonic() - start })
resp
}
They run outermost-first in the order given. Not calling next short-circuits, which is how authentication and a preflight response work.
Static files
web.static("/assets", "public/assets")
Traversal-safe: .. in a request cannot escape the directory. Use this rather than reading a request-supplied path with std.fs, which will happily serve /etc/passwd if you ask it to.
Errors
An unmatched route gives 404. A handler that raises gives 500 and logs. For designed error pages, per-route middleware, signed cookies, sessions, form validation and blueprints, use the webkit package, which is a full framework over this module.
Order
Routes match in order, so put specific paths before general ones. A static mount at / will shadow everything after it.