Every handler receives a *web.Ctx and returns an error:
type HandlerFunc func(c *Ctx) error*web.Ctx wraps http.ResponseWriter and *http.Request. It is per request: it must not be used after the handler returns or from other goroutines.
Request#
| Method | Returns |
|---|---|
Request(), SetRequest(r) | The underlying *http.Request |
Context() | The request context.Context |
Method(), Path() | Request method and path |
Param(name) | Path parameter ({name} in the pattern) |
ParamInt(name) | Path parameter as int64, or a 404 error when not numeric |
Query(name) | Query string value |
QueryInt(name, def) | Query value as int, or def when absent or invalid |
QueryBool(name) | true for 1, t, true… |
Header(name), Cookie(name) | Request header, cookie value ("" when absent) |
IP() | Client IP; X-Forwarded-For honoured only from trusted proxies |
IsJSON() | The request body is JSON |
WantsJSON() | The client prefers a JSON response |
IsHTMX() | The request was made by htmx (HX-Request: true) |
RouteName() | Name of the matched route |
Response#
| Method | Purpose |
|---|---|
Writer() | The *web.ResponseWriter (records status, size, whether it was written; supports http.ResponseController) |
SetHeader(name, value), SetCookie(ck) | Response headers and cookies |
Status(code) | Sets the status; chainable |
String, HTML, JSON, Blob, Stream | Bodies, see Responses |
Redirect, RedirectRoute, Back | Redirects |
Problem(status, title, detail) | RFC 9457 problem details |
Services#
The router owns one *web.Services value shared by every Ctx:
services := &web.Services{
Log: app.Log,
Debug: app.Config.Debug, // detailed 5xx messages, never in production
TrustedProxies: proxies, // []*net.IPNet for IP()
ProblemBaseURL: "https://example.com/problems",
// Errors: custom web.ErrorHandler; Views: a web.Renderer; URLs: set by the router
}
r := router.New(services)c.Services() returns them and c.Log() returns the request logger.
Trusted proxies#
c.IP() returns the direct peer address unless that peer is listed in TrustedProxies. Only then is X-Forwarded-For read, right to left, skipping trusted hops, so clients cannot spoof their address.
In an app built by vukem.New (every starter), set them from the environment; nobody is trusted by default and an invalid entry stops the boot:
TRUSTED_PROXIES=10.0.0.0/8,192.0.2.10
TRUST_FORWARDED_PROTO=true # X-Forwarded-Proto: https from those peers enables HSTS
CLIENT_IP_HEADER=X-Real-IP # optional: read this single-IP header instead of X-Forwarded-ForCLIENT_IP_HEADER (Services.ClientIPHeader) is for proxies that put the client address in one header of their own. It is read only from trusted peers and only when it holds exactly one IP; otherwise c.IP() returns the peer address. X-Forwarded-For is then ignored.
With a router you build yourself, set the field:
_, lb, _ := net.ParseCIDR("10.0.0.0/8")
services.TrustedProxies = []*net.IPNet{lb}