binder / Docs / API reference

API reference

The whole exported surface of uradical.io/go/binder: two functions, four types, one constant and five sentinel errors. The generated reference lives at pkg.go.dev; this page is the same material with the reasoning attached.

Functions

Bindfunc

func Bind(r *http.Request, i any) error

Binds an HTTP request into the struct i points at. The target must be a non-nil pointer to a struct; anything else is ErrInvalidTarget. Bodies are capped at DefaultMaxBodySize.

Every field is attempted, and every field that fails is reported together as BindErrors. Fields reflection cannot set — unexported ones — are ignored even when they carry a tag. If the target implements Validator, Validate runs once every field has bound, and its error is returned.

Returns an error when

  • any field fails to bind — BindErrors, one entry per failure, whether a value would not convert, a required value was missing (ErrMissingRequired) or, with DisallowUnknownFields, the body carried a key nothing binds (ErrUnknownField);
  • the target is not a non-nil pointer to a struct — ErrInvalidTarget;
  • the body exceeded the size limit — ErrBodyTooLarge;
  • the body could not be parsed — ErrMalformedBody;
  • the request was nil, or the target has a field that cannot be bound, such as a pointer type that points to itself, or one tagged binding, uri or param for another framework — ErrInvalidTarget;
  • the body could not be read at all, such as when the client disconnected — the I/O error, wrapped;
  • Validate failed — its error, wrapped.

BindWithOptionsfunc

func BindWithOptions(r *http.Request, i any, opts BindOptions) error

Bind with per-call configuration. The zero BindOptions behaves exactly as Bind does, so only the fields you care about need setting — a larger body limit on an upload endpoint, say, without touching any other.

A nil request is ErrInvalidTarget rather than a panic.

Types

BindOptionstype

type BindOptions struct {
	// MaxBodySize is the largest request body, in bytes, this call reads.
	// Zero applies DefaultMaxBodySize, and a negative value removes the limit.
	MaxBodySize int64

	// DisallowUnknownFields reports each top-level body key that no field of
	// the target binds, as a BindErrors entry wrapping ErrUnknownField. Keys
	// nested inside objects are not inspected.
	DisallowUnknownFields bool
}

See Options for how each behaves.

BindErrortype

type BindError struct {
	Field   string // Go field, as a path when nested: Address.Postcode, Tags[2]
	Source  string // tag source the value was read from, such as "query"
	Name    string // key the client sent, as a path when nested: address.postcode
	Message string // description for logs; its wording is not stable
	Err     error  // cause: ErrMissingRequired, ErrUnknownField or a conversion error
}

func (e *BindError) Error() string
func (e *BindError) Unwrap() error

One failure: a field that could not be bound, or a body member no field binds. Key client-facing output on Name, the key as the client sent it; it is empty only for an unknown member whose name was itself empty. Field is the Go-side name for logs, and is empty for an unknown body member, since no field binds it. See Error handling.

BindErrorstype

type BindErrors []*BindError

func (e BindErrors) Error() string   // one line per failure
func (e BindErrors) Unwrap() []error

What Bind returns when any field fails, whether one or several — the same shape either way, so a handler has one path. Entries follow the order the struct declares its fields, then any unknown body members, and there are at most 100 of them. Unwrap exposes each entry, so errors.Is(err, ErrMissingRequired) reports whether any field was missing. Reach it with errors.As.

Validatortype

type Validator interface {
	Validate(ctx context.Context) error
}

Implemented by a target that wants its own rules run once binding succeeds. Bind passes the request's context. See Validation.

Constants

DefaultMaxBodySizeconst

const DefaultMaxBodySize int64 = 10 << 20 // 10 MB

The largest body Bind reads, and the limit BindWithOptions applies when MaxBodySize is zero. A larger body is rejected with ErrBodyTooLarge rather than buffered. Its value is not part of the compatibility contract; pass BindOptions.MaxBodySize if your service depends on a particular limit.

Sentinel errors

Match these with errors.Is. The message text is not stable; these are.

SentinelReturned when
ErrMalformedBodyThe body could not be parsed as the format its Content-Type declares.
ErrBodyTooLargeThe body exceeded the size limit.
ErrInvalidTargetThe target was not a non-nil pointer to a struct, the request was nil, or the target has a field that cannot be bound, such as a pointer type that points to itself, or one tagged binding, uri or param for another framework.
ErrMissingRequiredA field tagged required had no value. Carried by a BindErrors entry.
ErrUnknownFieldDisallowUnknownFields was set and the body carried a key nothing binds. Carried by a BindErrors entry.

Struct tag summary

TagSource
path:"name"Path parameters, via Request.PathValue
query:"name"URL query parameters
body:"name"Request body: JSON, form-encoded or multipart, by Content-Type
json:"name"Request body; for tags shared with serialisation
cookie:"name"Request cookies
header:"name"Request headers, matched case-insensitively
form:"name"A form body's value when it has the key, otherwise the query's, as r.FormValue reads

Precedence when a field carries several: path, query, body, json, cookie, header. Options are ,omitempty and ,required. See Binding sources.