Documentation
¶
Overview ¶
Package handler is part of the GoFastr framework. See https://github.com/DonaldMurillo/gofastr for documentation.
Index ¶
- func Bind(r *http.Request, dst any) error
- func CheckObjectKeys(data []byte, fold func(string) string) error
- func CheckTopLevelKeys(data []byte, fold func(string) string) error
- func DecodeStrict(r io.Reader, dst any) error
- func GetLogger(ctx context.Context) (any, bool)
- func GetRequestID(ctx context.Context) (string, bool)
- func GetTenant(ctx context.Context) (any, bool)
- func GetUser(ctx context.Context) (any, bool)
- func HandlerAdapter[I, O any](h Handler[I, O]) http.HandlerFunc
- func RequestFromContext(ctx context.Context) (*http.Request, bool)
- func Respond(w http.ResponseWriter, r *http.Request, out any)
- func SSEStream(w http.ResponseWriter, events <-chan SSE)
- func SetLogger(ctx context.Context, logger any) context.Context
- func SetRequestID(ctx context.Context, id string) context.Context
- func SetTenant(ctx context.Context, tenant any) context.Context
- func SetUser(ctx context.Context, user any) context.Context
- func UnmarshalStrict(data []byte, dst any) error
- func WriteError(w http.ResponseWriter, err error)
- type Error
- type HTML
- type Handler
- type RawBytes
- type ResponseType
- type SSE
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Bind ¶
Bind populates dst by merging values from all sources: 1. Header values (via `header:"X-Request-ID"` tag) 2. Query parameters (via `query:"name"` tag) 3. Path parameters (via `path:"id"` tag) 4. JSON body (decodes entire struct, highest priority)
If the request has a JSON body and Content-Type is application/json, the body is decoded first. Then query/path/header values fill in any zero-valued fields.
If the body is present but not valid JSON, a 400 error is returned. If dst is not a pointer, Bind panics.
func CheckObjectKeys ¶ added in v0.82.0
CheckObjectKeys is CheckTopLevelKeys applied to every object at every nesting depth: no object anywhere in the value may repeat a key or hold two keys that fold to the same name. Non-object values and tokenisation failures return nil so the caller's decoder reports them under its own error contract.
func CheckTopLevelKeys ¶ added in v0.82.0
CheckTopLevelKeys walks the top level of a JSON object and refuses any key that repeats, or that collides with an earlier key once both are passed through fold. Callers with their own key normalisation (crud folds camelCase and snake_case spellings of one column onto each other) pass that normaliser as fold; nil means exact keys only. Non-object bodies and tokenisation failures return nil so the caller's decoder reports them under its own error contract.
func DecodeStrict ¶ added in v0.82.0
DecodeStrict reads one JSON value from r into dst with the same top-level key rule Bind applies to request bodies, for every decode site that is not a plain handler.Bind consumer: JSON-RPC envelopes, websocket frames, buffered bodies replayed through a helper, dev-tool endpoints. Stdlib encoding/json keeps the LAST duplicate key and matches struct tags case-insensitively, so {"action":"safe", "Action":"danger"} runs the second action while a reviewer (or an intercepting log) reads the first. The ambiguity itself is the bug; refusing it is the only resolution that does not privilege one parser's pick.
Struct destinations: every top-level key must exactly match a json tag (case-folded spellings and unknown keys are refused, as Bind does) and no key may repeat. Map and other destinations: no key may repeat, and no two keys may fold to the same name under ASCII case folding. Bodies that are not a top-level object decode unchanged. Errors are *Error with code 400, so a handler can hand them to respond/Error directly.
Size is the caller's job: wrap the reader in http.MaxBytesReader (or io.LimitReader) before calling. The probes that pinned each surface live beside the callers as *_security_test.go.
func GetRequestID ¶
GetRequestID retrieves the request ID from the context.
func HandlerAdapter ¶
func HandlerAdapter[I, O any](h Handler[I, O]) http.HandlerFunc
HandlerAdapter bridges a typed Handler into a standard http.HandlerFunc. It handles input binding, panic recovery, and output serialization.
func RequestFromContext ¶
RequestFromContext retrieves the *http.Request stored by HandlerAdapter.
func Respond ¶
func Respond(w http.ResponseWriter, r *http.Request, out any)
Respond writes out the response based on out's type:
- nil → 204 No Content
- ResponseType → delegates to the custom type
- any other value → JSON serialization, 200 OK
func SSEStream ¶
func SSEStream(w http.ResponseWriter, events <-chan SSE)
SSEStream writes a channel of SSE events as a streaming response.
func SetRequestID ¶
SetRequestID stores a request ID in the context.
func UnmarshalStrict ¶ added in v0.82.0
UnmarshalStrict is DecodeStrict over bytes already in memory (a websocket frame, a buffered body).
func WriteError ¶
func WriteError(w http.ResponseWriter, err error)
WriteError writes a structured error response to w.
An *Error is rendered verbatim (its Code, Message, and Fields are what the developer chose to expose). Any other error type is treated as an internal failure: the response message is a generic "internal server error" with status 500, and the original error stays out of the response body. Callers that *do* want the inner message to reach the client must wrap explicitly via Errorf or WrapError (the latter keeps the cause for `errors.Is/As` without leaking it).
This prevents accidental leaks of database errors, driver-specific strings ("pq: password authentication failed for user \"admin\""), or wrapped stack traces.
Types ¶
type Error ¶
type Error struct {
Code int // HTTP status code
Message string // human-readable message
Err error // wrapped cause (optional)
Fields map[string][]string // field-level validation errors (optional)
}
Error is a structured HTTP error with optional field-level validation errors.
func ValidationError ¶
ValidationError creates a 400 error with field-level validation messages.
type HTML ¶
type HTML string
HTML is a response type that writes text/html.
func (HTML) ContentType ¶
type Handler ¶
Handler is a typed function that processes an input of type I and returns an output of type O. It receives a context (which carries the *http.Request via RequestFromContext) and a fully-bound input struct.
type RawBytes ¶
RawBytes is a response type for raw bytes with an explicit content type.
func (RawBytes) ContentType ¶
type ResponseType ¶
type ResponseType interface {
// ContentType returns the MIME type for the response.
ContentType() string
// WriteBody writes the response body to w.
WriteBody(w http.ResponseWriter) error
}
ResponseType is an interface for custom response types that control how they are serialized and what Content-Type is used.