Documentation
¶
Overview ¶
Package web provides the request context (*Ctx) used by Vukem handlers: request helpers, typed input, responses, and the central error handler.
A handler has the signature func(*Ctx) error. Returning an error hands it to the error handler, which renders the right response for the client (HTML, redirect with flashed errors, HTMX fragment or problem+json).
Parse, ParseQuery and Bind turn request input into typed values through z schemas; Services carries the shared dependencies (URL generator, view renderer, logger, authorizer) the router hands to every Ctx.
See https://github.com/AlexGalhardo/vukem/blob/main/docs/http.md.
Example ¶
package main
import (
"fmt"
"log/slog"
"net/http"
"net/http/httptest"
"strings"
"github.com/AlexGalhardo/vukem/errs"
"github.com/AlexGalhardo/vukem/web"
)
// serve runs h the way the router does: errors go to the error handler.
func serve(h web.HandlerFunc, req *http.Request) *httptest.ResponseRecorder {
rec := httptest.NewRecorder()
web.NewCtx(rec, req, &web.Services{Log: slog.New(slog.DiscardHandler)}).Serve(h)
return rec
}
func main() {
show := func(c *web.Ctx) error {
if c.Query("id") != "1" {
return errs.ErrNotFound
}
return c.JSON(http.StatusOK, web.Map{"id": 1, "title": "Hello"})
}
ok := httptest.NewRequest(http.MethodGet, "/posts?id=1", http.NoBody)
rec := serve(show, ok)
fmt.Println(rec.Code, strings.TrimSpace(rec.Body.String()))
missing := httptest.NewRequest(http.MethodGet, "/posts?id=2", http.NoBody)
missing.Header.Set("Accept", "application/json")
rec = serve(show, missing)
fmt.Println(rec.Code, rec.Header().Get("Content-Type"))
}
Output: 200 {"id":1,"title":"Hello"} 404 application/problem+json
Index ¶
- Constants
- func AuditEvent(r *http.Request, event string, attrs ...any)
- func Bind[T any](c *Ctx) (T, error)
- func BodyLimitOf(r *http.Request) (int64, bool)
- func CSPNonce(ctx context.Context) string
- func DefaultErrorHandler(c *Ctx, err error)
- func ETagOf(v any) (string, error)
- func ErrorsFrom(ctx context.Context) map[string][]string
- func Fail(w http.ResponseWriter, r *http.Request, err error)
- func FlashFrom(ctx context.Context, key string) any
- func FormToMap(values url.Values) map[string]any
- func IsJSONMediaType(contentType string) bool
- func IssueLocalizer(ctx context.Context) func(z.Issues) z.Issues
- func JSONWith[T any](c *Ctx, status int, schema z.Schema[T], v T) error
- func LimitBody(w http.ResponseWriter, r *http.Request, n int64) *http.Request
- func OldInput(ctx context.Context, field string) string
- func Parse[T any](c *Ctx, schema z.Schema[T]) (T, error)
- func ParseInput[T any](schema z.Schema[T], input map[string]any) (T, error)
- func ParseMap[T any](c *Ctx, schema z.Schema[T], input map[string]any) (T, error)
- func ParseParams[T any](c *Ctx, schema z.Schema[T], names ...string) (T, error)
- func ParseQuery[T any](c *Ctx, schema z.Schema[T]) (T, error)
- func RequestID(ctx context.Context) string
- func SafeRedirect(r *http.Request, target, fallback string) string
- func SecurityEvent(r *http.Request, event string, attrs ...any)
- func WithCSPNonce(ctx context.Context, nonce string) context.Context
- func WithIssueLocalizer(ctx context.Context, fn func(z.Issues) z.Issues) context.Context
- func WithRequestID(ctx context.Context, id string) context.Context
- func WithServices(ctx context.Context, s *Services) context.Context
- func WithSession(ctx context.Context, s Session) context.Context
- func WithValidationHook(ctx context.Context, hook ValidationHook) context.Context
- type Authorizer
- type Ctx
- func (c *Ctx) Accepted(v any) error
- func (c *Ctx) Authorize(ability string, subject any) error
- func (c *Ctx) Back(fallback string) error
- func (c *Ctx) BindJSON(v any) error
- func (c *Ctx) Blob(status int, contentType string, b []byte) error
- func (c *Ctx) CheckIfMatch(current string) error
- func (c *Ctx) Context() context.Context
- func (c *Ctx) Cookie(name string) string
- func (c *Ctx) Created(location string, v any) error
- func (c *Ctx) Download(fsys fs.FS, name, filename string) error
- func (c *Ctx) Errors() map[string][]string
- func (c *Ctx) File(fsys fs.FS, name string) error
- func (c *Ctx) Flash(key string, value any) error
- func (c *Ctx) FlashValue(key string) any
- func (c *Ctx) FormFile(name string) (multipart.File, *multipart.FileHeader, error)
- func (c *Ctx) FormValue(name string) string
- func (c *Ctx) Fragment(name, block string, data any, opts ...RenderOption) error
- func (c *Ctx) HTML(status int, html string) error
- func (c *Ctx) HTMX() *htmx.Response
- func (c *Ctx) HandleError(err error)
- func (c *Ctx) Header(name string) string
- func (c *Ctx) IP() string
- func (c *Ctx) Input() (map[string]any, error)
- func (c *Ctx) IsHTMX() bool
- func (c *Ctx) IsHTTPS() bool
- func (c *Ctx) IsJSON() bool
- func (c *Ctx) IsPartial() bool
- func (c *Ctx) JSON(status int, v any) error
- func (c *Ctx) Log() *slog.Logger
- func (c *Ctx) Method() string
- func (c *Ctx) MustURL(name string, params ...any) string
- func (c *Ctx) NoContent() error
- func (c *Ctx) NotModified(etag string, lastModified time.Time) bool
- func (c *Ctx) Old(field string) string
- func (c *Ctx) Param(name string) string
- func (c *Ctx) ParamInt(name string) (int64, error)
- func (c *Ctx) Path() string
- func (c *Ctx) Problem(status int, title, detail string) error
- func (c *Ctx) Query(name string) string
- func (c *Ctx) QueryBool(name string) bool
- func (c *Ctx) QueryInput() map[string]any
- func (c *Ctx) QueryInt(name string, def int) int
- func (c *Ctx) Redirect(target string) error
- func (c *Ctx) RedirectRoute(name string, params ...any) error
- func (c *Ctx) RedirectStatus(status int, target string) error
- func (c *Ctx) Render(name string, data any, opts ...RenderOption) error
- func (c *Ctx) Request() *http.Request
- func (c *Ctx) RouteName() string
- func (c *Ctx) SSE(fn func(*sse.Stream) error, opts ...sse.Option) error
- func (c *Ctx) Serve(h HandlerFunc)
- func (c *Ctx) Services() *Services
- func (c *Ctx) Session() Session
- func (c *Ctx) SetCookie(ck *http.Cookie)
- func (c *Ctx) SetHeader(name, value string)
- func (c *Ctx) SetRequest(r *http.Request)
- func (c *Ctx) SetRouteName(name string)
- func (c *Ctx) Status(code int) *Ctx
- func (c *Ctx) Stream(status int, contentType string, r io.Reader) error
- func (c *Ctx) String(status int, s string) error
- func (c *Ctx) URL(name string, params ...any) (string, error)
- func (c *Ctx) View(v View, opts ...RenderOption) error
- func (c *Ctx) WantsJSON() bool
- func (c *Ctx) Writer() *ResponseWriter
- type ErrorHandler
- type ErrorPage
- type ErrorPageFunc
- type EventDispatcher
- type HandlerFunc
- type Map
- type Problem
- type RenderOption
- type Renderer
- type ResponseWriter
- func (w *ResponseWriter) Flush()
- func (w *ResponseWriter) FlushError() error
- func (w *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error)
- func (w *ResponseWriter) Size() int64
- func (w *ResponseWriter) Status() int
- func (w *ResponseWriter) Unwrap() http.ResponseWriter
- func (w *ResponseWriter) Write(b []byte) (int, error)
- func (w *ResponseWriter) WriteHeader(code int)
- func (w *ResponseWriter) WriteString(s string) (int, error)
- func (w *ResponseWriter) Written() bool
- type Services
- type Session
- type URLGenerator
- type ValidationHook
- type View
Examples ¶
Constants ¶
const ( OldInputKey = "_old" ErrorsKey = "_errors" TokenKey = "_token" // SessionUserKey holds the id of the logged-in user (auth guards set it // on login) so server-side stores can revoke a user's sessions. SessionUserKey = "_user_id" )
Reserved session keys used by flash, old input and validation errors.
const DefaultMaxBody = 10 << 20
DefaultMaxBody is the request body limit applied by Input and BindJSON when no smaller limit was set by middleware (A06: resource exhaustion).
Variables ¶
This section is empty.
Functions ¶
func AuditEvent ¶ added in v0.26.0
AuditEvent logs a routine audit record (an admin recorded a payout, changed a role) at INFO with the same envelope as SecurityEvent: a stable "event" attribute, method, path and request_id. It is dispatched as a security.Audit, so security alerts are not mixed with routine records. Pass the actor as an attribute ("actor_id", u.AuthID()); never pass secrets (a config.Secret is logged masked) or personal data such as names and emails. The dispatched record carries the client IP, which is personal data: stored audit records need a retention period.
func Bind ¶
Bind parses the request input into T using the schema derived from T's `validate` struct tags (z.For).
func BodyLimitOf ¶ added in v0.19.0
BodyLimitOf returns the body limit set by LimitBody, if any.
func DefaultErrorHandler ¶
DefaultErrorHandler renders err according to the client:
- JSON clients get RFC 9457 problem+json (422 includes field errors; z.Issues and z.FieldError errors count as validation errors);
- classic form posts with a validation error go to the validation hook (flash errors and redirect back) when one is installed;
- htmx requests get an HTML fragment with the status (htmx 4 swaps 4xx);
- Services.ErrorPage, when set, renders the page in its own format (an Inertia page for Inertia requests);
- browsers get an HTML status page (views "errors/<status>" if present).
5xx details are never sent to clients unless Services.Debug is set (docs/security.md, A10).
func ETagOf ¶
ETagOf returns a strong ETag ("\"...\"") for v: the SHA-256 of its JSON encoding (or of the bytes when v is []byte or string).
func ErrorsFrom ¶ added in v0.19.0
ErrorsFrom is Ctx.Errors for code that only has the request context, such as templ components.
func Fail ¶
func Fail(w http.ResponseWriter, r *http.Request, err error)
Fail renders err through the error handler. Middleware uses it to reject a request (401, 413, 415, 429...) with the same format as handlers.
func FlashFrom ¶ added in v0.19.0
FlashFrom returns the value flashed under key by the previous request, or nil. It is Ctx.FlashValue for code that only has the request context, such as templ components.
func FormToMap ¶
FormToMap converts url.Values into nested maps and lists: "a=1&a=2" → {"a": ["1","2"]}, "tags[]=x" → {"tags": ["x"]}, "author[email]=e" → {"author": {"email": "e"}}, "items[0][name]=n" → {"items": [{"name": "n"}]}.
func IsJSONMediaType ¶ added in v0.21.0
IsJSONMediaType reports whether a Content-Type value is application/json or an application/*+json type. Parameters are ignored, so a CORS-simple "text/plain; x=+json" body is never taken for JSON.
func IssueLocalizer ¶ added in v0.18.0
IssueLocalizer returns the function stored by WithIssueLocalizer, or nil.
func JSONWith ¶
JSONWith writes v as JSON after checking it against schema when Services.CheckResponses is enabled (development and test by default). A mismatch is a server bug: it is logged and answered with 500 instead of sending data that breaks the API contract.
func LimitBody ¶
LimitBody caps the request body at n bytes and marks the request so handlers do not apply DefaultMaxBody on top. Body-limit middleware uses it. Called again on the same request, the new limit replaces the old one, so a route can raise or lower the router-wide default.
A declared Content-Length above n fails on the first read, before any byte is consumed, with an *http.MaxBytesError; undeclared (chunked) bodies fail once they cross n.
func OldInput ¶ added in v0.19.0
OldInput is Ctx.Old for code that only has the request context, such as templ components.
func Parse ¶
Parse reads the request input (JSON, form or multipart body merged over the query string) and parses it with schema. Validation issues become an *errs.ValidationError, so returning the error from a handler yields a 422 problem+json, an HTMX fragment or a redirect back with flashed errors.
in, err := web.Parse(c, requests.StorePostSchema)
if err != nil {
return err
}
Example ¶
package main
import (
"fmt"
"log/slog"
"net/http"
"net/http/httptest"
"strings"
"github.com/AlexGalhardo/vukem/web"
"github.com/AlexGalhardo/vukem/z"
)
type createPost struct {
Title string `json:"title"`
}
var createPostSchema = z.Struct[createPost](z.Shape{"title": z.String().Trim().Min(3)})
// serve runs h the way the router does: errors go to the error handler.
func serve(h web.HandlerFunc, req *http.Request) *httptest.ResponseRecorder {
rec := httptest.NewRecorder()
web.NewCtx(rec, req, &web.Services{Log: slog.New(slog.DiscardHandler)}).Serve(h)
return rec
}
func main() {
store := func(c *web.Ctx) error {
in, err := web.Parse(c, createPostSchema)
if err != nil {
return err // 422 problem+json, HTMX fragment or redirect back
}
return c.String(http.StatusCreated, "created "+in.Title)
}
req := httptest.NewRequest(http.MethodPost, "/posts", strings.NewReader(`{"title":" Gophers "}`))
req.Header.Set("Content-Type", "application/json")
rec := serve(store, req)
fmt.Println(rec.Code, rec.Body.String())
req = httptest.NewRequest(http.MethodPost, "/posts", strings.NewReader(`{"title":"Go"}`))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
rec = serve(store, req)
fmt.Println(rec.Code)
}
Output: 201 created Gophers 422
func ParseInput ¶ added in v0.15.0
ParseInput validates an already assembled input map (validation errors become a 422 ValidationError). Messages stay in English and Refine errors are not logged: it has no request to read them from. Prefer ParseMap inside a handler.
func ParseMap ¶ added in v0.28.0
ParseMap validates an already assembled input map with the request's context and logger: validation errors become a localized 422 ValidationError (see WithIssueLocalizer) and the raw errors of failed Refine/Custom checks are logged, never sent to the client.
func ParseParams ¶
ParseParams parses the named path parameters ({id}, {slug}...).
func ParseQuery ¶
ParseQuery parses only the query string.
func SafeRedirect ¶
SafeRedirect returns target when it is a relative path or has the same host as r, and fallback otherwise.
func SecurityEvent ¶
SecurityEvent logs a security-relevant event at WARN with a stable "event" attribute (docs/security.md, A09) and dispatches it as a security.Event.
func WithCSPNonce ¶ added in v0.18.0
WithCSPNonce stores the request's Content-Security-Policy nonce in ctx (middleware.SecureHeaders does this).
func WithIssueLocalizer ¶ added in v0.18.0
WithIssueLocalizer stores fn in ctx; Parse, Bind, ParseQuery and ParseParams pass validation issues through it before building the *errs.ValidationError, so field messages follow the request locale. The i18n middleware installs one bound to the resolved locale.
func WithRequestID ¶
WithRequestID stores the request id in ctx.
func WithServices ¶
WithServices stores s in ctx so middleware can render errors consistently.
func WithSession ¶
WithSession stores s in ctx (session middleware does this).
func WithValidationHook ¶
func WithValidationHook(ctx context.Context, hook ValidationHook) context.Context
WithValidationHook installs hook for requests carrying ctx.
Types ¶
type Authorizer ¶ added in v0.6.0
Authorizer decides abilities for the user in ctx: nil, or an error with status 401, 403 or 404.
type Ctx ¶
type Ctx struct {
// contains filtered or unexported fields
}
Ctx is the per-request context. It must not be used after the handler returns or from other goroutines.
func (*Ctx) Authorize ¶ added in v0.6.0
Authorize checks ability on subject (nil for gate abilities) with Services.Authorizer and returns the error to return from the handler. Denials are logged as "authz.denied" security events. Without an Authorizer it denies (fail closed).
func (*Ctx) Back ¶
Back redirects to the Referer when it is same-origin, else to fallback. Off-site referers are ignored (open-redirect protection, A01).
func (*Ctx) BindJSON ¶
BindJSON decodes the JSON body into v, rejecting unknown fields (422 with a field error per key, for struct targets), trailing data and bodies over the size limit.
func (*Ctx) CheckIfMatch ¶
CheckIfMatch enforces optimistic concurrency for writes: when the client sent If-Match and it does not match the current ETag, it returns a 412 error. Without If-Match it returns nil.
func (*Ctx) Download ¶
Download serves name from fsys as an attachment called filename (the base of name when empty). The name is sanitized like export.ContentDisposition: last path element only, no control or bidi characters, an RFC 5987 filename* when it is not ASCII. The header is set only once the file opened, so an error page is not an attachment.
func (*Ctx) FlashValue ¶
FlashValue returns a flashed value from the previous request, or nil.
func (*Ctx) FormFile ¶
FormFile returns an uploaded file. A missing file, or a request that is not multipart/form-data (a form without enctype, a JSON body), is a 422 validation error on name.
func (*Ctx) Fragment ¶
func (c *Ctx) Fragment(name, block string, data any, opts ...RenderOption) error
Fragment renders a single block of a view.
func (*Ctx) HTML ¶
HTML writes a text/html response from a trusted string. Never pass user input: use views (html/template) for escaping.
func (*Ctx) HandleError ¶
HandleError passes err to the error handler.
func (*Ctx) IP ¶
IP returns the client IP. X-Forwarded-For (or ClientIPHeader when set) is honoured only when the direct peer is a trusted proxy (docs/security.md, A01/A02).
func (*Ctx) Input ¶
Input returns the request body (JSON object, urlencoded or multipart form) merged over the query string as a generic map ready for schema parsing: JSON keeps its types (numbers as json.Number); form values are strings, repeated or "[]" keys become lists and "a[b]" keys nest.
func (*Ctx) IsHTTPS ¶ added in v0.28.0
IsHTTPS reports whether the client reached the app over HTTPS: the connection is TLS, or TrustForwardedProto is set and a TrustedProxies peer sent a single X-Forwarded-Proto: https line (a second line may come from the client).
func (*Ctx) MustURL ¶
MustURL is URL for templates and code paths where the route is known to exist; it returns "#" and logs on error instead of panicking.
func (*Ctx) NotModified ¶
NotModified sets the ETag (and Last-Modified when non-zero) headers and reports whether the client's cached copy is current. When it returns true a 304 was written and the handler must return without a body:
if c.NotModified(etag, post.UpdatedAt) { return nil }
func (*Ctx) Old ¶
Old returns the input submitted by the previous (failed) form post for field, so forms can be re-filled. Nested fields use dots ("author.email").
func (*Ctx) ParamInt ¶
ParamInt returns a path parameter as int64, or a 404 error when it is not a valid integer (a non-numeric id names no resource).
func (*Ctx) QueryInput ¶
QueryInput returns the query string as a generic map (see Input).
func (*Ctx) Redirect ¶
Redirect redirects to target. Unsafe methods get 303 See Other so the browser follows with GET; GET/HEAD get 302 Found. Use RedirectStatus for other codes. htmx requests receive HX-Location instead (navigation without a full reload).
func (*Ctx) RedirectRoute ¶
RedirectRoute redirects to a named route.
func (*Ctx) RedirectStatus ¶
RedirectStatus redirects with an explicit 3xx status.
func (*Ctx) Render ¶
func (c *Ctx) Render(name string, data any, opts ...RenderOption) error
Render renders a view into the response. Output is buffered, so a template error becomes a clean 500 instead of a half-written page.
func (*Ctx) SSE ¶ added in v0.18.0
SSE streams server-sent events to the client with fn (see package sse):
return c.SSE(func(s *sse.Stream) error { return hub.Stream(s, "posts") })
func (*Ctx) Serve ¶
func (c *Ctx) Serve(h HandlerFunc)
Serve runs h with c and routes a returned error to the error handler. A status set with Status and not yet sent is sent when h returns nil.
func (*Ctx) SetRequest ¶
SetRequest replaces the request (e.g. after adding context values).
func (*Ctx) SetRouteName ¶
SetRouteName is used by routers to record the matched route.
func (*Ctx) Status ¶
Status sets the response status without sending it: the next helper that writes the response with the default 200 (JSON(200, v), Render, View, inertia.Render, a plain Write) sends it instead, with its headers. c.Status(201).JSON(200, v) answers 201 with Content-Type application/json. Any other status wins over it: a redirect, NoContent, a 304, JSON(202, v). A handler that returns nil without writing sends the status alone; a returned error or a panic answers with its own status.
func (*Ctx) View ¶
func (c *Ctx) View(v View, opts ...RenderOption) error
View renders a component such as a templ.Component.
type ErrorHandler ¶
ErrorHandler writes the response for an error returned by a handler.
type ErrorPage ¶ added in v0.27.0
type ErrorPage struct {
// Status is the HTTP status.
Status int
// Title is the status text, or "Validation failed" for a 4xx with
// field errors.
Title string
// Detail is the error's own (translated) message for 4xx; empty when
// it would repeat Title, and always empty for 5xx.
Detail string
// Errors holds the field errors of a 4xx validation error (all
// messages per field); nil otherwise.
Errors map[string][]string
}
ErrorPage is the client-safe description of an error that DefaultErrorHandler hands to Services.ErrorPage. Its text never holds a 5xx cause or message, nor a wrapped cause (CWE-209).
type ErrorPageFunc ¶ added in v0.27.0
ErrorPageFunc renders an error page described by e. It reports whether it wrote a response; false falls back to the HTML page.
type EventDispatcher ¶ added in v0.13.0
EventDispatcher delivers security events to listeners (an events.Bus).
type HandlerFunc ¶
HandlerFunc handles a request. Returned errors go to the error handler.
type Problem ¶
type Problem struct {
Type string `json:"type"`
Title string `json:"title"`
Status int `json:"status"`
Detail string `json:"detail,omitempty"`
Instance string `json:"instance,omitempty"`
RequestID string `json:"request_id,omitempty"`
Errors map[string][]string `json:"errors,omitempty"`
}
Problem is an RFC 9457 problem details document.
type RenderOption ¶
type RenderOption func(*renderOpts)
RenderOption configures Render.
func Block ¶
func Block(name string) RenderOption
Block renders only the named block for htmx partial requests and the full page otherwise (template fragments):
return c.Render("posts/index", data, web.Block("post-list"))
func Partial ¶ added in v0.19.0
func Partial(v View) RenderOption
Partial is Block for View: c.View renders v instead of the page for htmx partial requests (templ fragments):
return c.View(views.PostsIndex(posts), web.Partial(views.PostList(posts)))
func WithStatus ¶
func WithStatus(code int) RenderOption
WithStatus sets the response status (default 200).
type Renderer ¶
type Renderer interface {
Render(ctx context.Context, w io.Writer, name string, data any) error
RenderBlock(ctx context.Context, w io.Writer, name, block string, data any) error
Has(name string) bool
}
Renderer renders named views. The view package implements it; ctx gives templates access to request-scoped data (session, CSRF token, CSP nonce).
type ResponseWriter ¶
type ResponseWriter struct {
http.ResponseWriter
// contains filtered or unexported fields
}
ResponseWriter wraps http.ResponseWriter to record the status and whether the response was written. It supports http.ResponseController.
func (*ResponseWriter) Flush ¶ added in v0.28.0
func (w *ResponseWriter) Flush()
Flush sends the status (a pending Ctx.Status, else 200) and flushes the underlying writer. Without it, http.ResponseController would flush the unwrapped writer and send an implicit 200 behind this one's back.
func (*ResponseWriter) FlushError ¶ added in v0.28.0
func (w *ResponseWriter) FlushError() error
FlushError is Flush reporting http.ErrNotSupported when the underlying writer cannot flush (http.ResponseController prefers it).
func (*ResponseWriter) Hijack ¶ added in v0.28.0
func (w *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error)
Hijack hands the connection to the caller (WebSocket upgrades). No status is sent: the hijacker writes its own response, and the writer counts as written so nothing is sent on the connection afterwards.
func (*ResponseWriter) Size ¶
func (w *ResponseWriter) Size() int64
Size returns the number of body bytes written.
func (*ResponseWriter) Status ¶
func (w *ResponseWriter) Status() int
Status returns the written status (0 when nothing was written).
func (*ResponseWriter) Unwrap ¶
func (w *ResponseWriter) Unwrap() http.ResponseWriter
Unwrap supports http.ResponseController.
func (*ResponseWriter) Write ¶
func (w *ResponseWriter) Write(b []byte) (int, error)
Write writes the body, sending a 200 status first when needed.
func (*ResponseWriter) WriteHeader ¶
func (w *ResponseWriter) WriteHeader(code int)
WriteHeader records and writes the status. A status set earlier with Ctx.Status replaces a 200 (the implicit status of Write, and the default of helpers such as Render, View or inertia.Render); any other code (a redirect, 204, 304, an explicit 202) is sent as given.
func (*ResponseWriter) WriteString ¶ added in v0.20.1
func (w *ResponseWriter) WriteString(s string) (int, error)
WriteString writes s like Write, without copying it into a []byte when the underlying writer is an io.StringWriter.
func (*ResponseWriter) Written ¶
func (w *ResponseWriter) Written() bool
Written reports whether the status line was sent.
type Services ¶
type Services struct {
URLs URLGenerator
Views Renderer
Errors ErrorHandler
Log *slog.Logger
// Debug enables detailed error pages (never in production).
Debug bool
// DebugOut receives a colored report of every 5xx error when Debug is
// on (the app sets os.Stderr in development).
DebugOut io.Writer
// TrustedProxies lists CIDRs whose X-Forwarded-For is honoured by IP().
TrustedProxies []*net.IPNet
// ClientIPHeader, when set, names a header (such as X-Real-IP) that a
// trusted proxy sets to the client IP. IP() then reads it instead of
// X-Forwarded-For, only from TrustedProxies peers; a missing, repeated
// or non-IP value falls back to the peer address.
ClientIPHeader string
// TrustForwardedProto makes IsHTTPS honour X-Forwarded-Proto: https,
// only from TrustedProxies peers (TRUST_FORWARDED_PROTO).
TrustForwardedProto bool
// IPv6Prefix is the prefix length per-IP keys (ratelimit.ByIP) group
// IPv6 clients by, since one client usually owns a whole /64; 0 or any
// value outside 1-128 means 64. IPv4 (and IPv4-mapped IPv6) keys stay
// per address.
IPv6Prefix int
// ProblemBaseURL prefixes RFC 9457 problem "type" URIs; empty means
// "about:blank".
ProblemBaseURL string
// CheckResponses makes JSONWith validate outgoing data against its
// schema (enabled outside production by the app).
CheckResponses bool
// Events receives a security.Event for every SecurityEvent (alerting)
// and a security.Audit for every AuditEvent.
Events EventDispatcher
// Authorizer backs Ctx.Authorize and the "can" template function
// (an authz.Gate). Without one, every authorization check denies.
Authorizer Authorizer
// ErrorPage lets a frontend adapter render DefaultErrorHandler's error
// page in its own format (vukem.WithInertia installs
// inertia.Inertia.ErrorPage, an Inertia page for Inertia requests).
// Nil keeps the HTML page.
ErrorPage ErrorPageFunc
}
Services are the shared dependencies every Ctx can reach. The router owns one Services value; fields may be nil when the feature is not installed.
func ServicesFrom ¶
ServicesFrom returns the services stored in ctx, or an empty value.
type Session ¶
type Session interface {
// Get returns a value, or nil.
Get(key string) any
// Put stores a value (JSON-compatible) for this and later requests.
Put(key string, value any)
// Forget removes keys.
Forget(keys ...string)
// Pull returns and removes a value.
Pull(key string) any
// Flash stores a value available only on the next request.
Flash(key string, value any)
// Regenerate issues a new session id keeping the data (call on login
// and privilege changes to prevent session fixation).
Regenerate()
// Invalidate clears all data and issues a new id (call on logout).
Invalidate()
// ID returns the session id (meaningful for server-side stores).
ID() string
}
Session is the request session as seen by handlers. The session package implements it; web only depends on this interface.
func SessionFrom ¶
SessionFrom returns the session stored in ctx, or nil.
type URLGenerator ¶
URLGenerator builds URLs for named routes.
type ValidationHook ¶
type ValidationHook func(c *Ctx, verr *errs.ValidationError) bool
ValidationHook lets session-aware code (flash + redirect back) handle validation errors for classic HTML form posts. It reports whether it wrote a response.