Documentation
¶
Overview ¶
Package binding provides request data binding for HTTP handlers.
The binding package maps values from various sources (query parameters, form data, JSON bodies, headers, cookies, path parameters) into Go structs using struct tags. It supports nested structs, slices, maps, pointers, custom types, default values, enum validation, and type conversion.
Quick Start ¶
The package provides both generic and non-generic APIs:
// Generic (preferred when type is known) user, err := binding.JSON[CreateUserRequest](body) // Non-generic (when type comes from variable) var user CreateUserRequest err := binding.JSONTo(body, &user)
Source-Specific Functions ¶
Each binding source has dedicated functions:
// Query parameters params, err := binding.Query[ListParams](r.URL.Query()) // Path parameters params, err := binding.Path[GetUserParams](pathParams) // Form data data, err := binding.Form[FormData](r.PostForm) // HTTP headers headers, err := binding.Header[RequestHeaders](r.Header) // Cookies session, err := binding.Cookie[SessionData](r.Cookies()) // JSON body user, err := binding.JSON[CreateUserRequest](body) // XML body user, err := binding.XML[CreateUserRequest](body)
Multi-Source Binding ¶
Bind from multiple sources using From* options:
req, err := binding.Bind[CreateOrderRequest](
binding.FromPath(pathParams),
binding.FromQuery(r.URL.Query()),
binding.FromHeader(r.Header),
binding.FromJSON(body),
)
Configuration ¶
Use functional options to customize binding behavior:
user, err := binding.JSON[User](body,
binding.WithUnknownFields(binding.UnknownError),
binding.WithRequired(),
binding.WithMaxDepth(16),
)
Reusable Binder ¶
For shared configuration, create a Binder instance:
binder := binding.MustNew(
binding.WithConverter[uuid.UUID](uuid.Parse),
binding.WithTimeLayouts("2006-01-02", "01/02/2006"),
binding.WithRequired(),
)
// Use across handlers
user, err := binder.JSON[CreateUserRequest](body)
params, err := binder.Query[ListParams](r.URL.Query())
Struct Tags ¶
The package uses struct tags to map values:
type Request struct {
// Query parameters
Page int `query:"page" default:"1"`
Limit int `query:"limit" default:"20"`
// Path parameters
UserID string `path:"user_id"`
// Headers
Auth string `header:"Authorization"`
// JSON body fields
Name string `json:"name" required:"true"`
Email string `json:"email" required:"true"`
// Enum validation
Status string `json:"status" enum:"active,pending,disabled"`
}
Supported Tag Types ¶
- query: URL query parameters (?name=value)
- path: URL path parameters (/users/:id)
- form: Form data (application/x-www-form-urlencoded)
- header: HTTP headers
- cookie: HTTP cookies
- json: JSON body fields
- xml: XML body fields
Additional Serialization Formats ¶
The following formats are available as sub-packages:
- rivaas.dev/binding/yaml: YAML support (gopkg.in/yaml.v3)
- rivaas.dev/binding/toml: TOML support (github.com/BurntSushi/toml)
- rivaas.dev/binding/msgpack: MessagePack support (github.com/vmihailenco/msgpack/v5)
- rivaas.dev/binding/proto: Protocol Buffers support (google.golang.org/protobuf)
Example with YAML:
import "rivaas.dev/binding/yaml" config, err := yaml.YAML[Config](body)
Example with TOML:
import "rivaas.dev/binding/toml" config, err := toml.TOML[Config](body)
Example with MessagePack:
import "rivaas.dev/binding/msgpack" msg, err := msgpack.MsgPack[Message](body)
Example with Protocol Buffers:
import "rivaas.dev/binding/proto" user, err := proto.Proto[*pb.User](body)
Special Tags ¶
- default:"value": Default value when field is not present
- enum:"a,b,c": Validate value is one of the allowed values
- required:"true": Field must be present (when WithRequired() is used)
Type Conversion ¶
Built-in support for common types:
- Primitives: string, int*, uint*, float*, bool
- Time: time.Time, time.Duration
- Network: net.IP, net.IPNet, url.URL
- Slices: []T for any supported T
- Maps: map[string]T for any supported T
- Pointers: *T for any supported T
- encoding.TextUnmarshaler implementations
Register custom converters:
binding.MustNew(
binding.WithConverter[uuid.UUID](uuid.Parse),
binding.WithConverter[decimal.Decimal](decimal.NewFromString),
)
Error Handling ¶
Errors provide detailed context:
user, err := binding.JSON[User](body)
if err != nil {
var bindErr *binding.BindError
if errors.As(err, &bindErr) {
fmt.Printf("Field: %s, Source: %s, Value: %s\n",
bindErr.Field, bindErr.Source, bindErr.Value)
}
}
Collect all errors instead of failing on first:
user, err := binding.JSON[User](body, binding.WithAllErrors())
if err != nil {
var multi *binding.MultiError
if errors.As(err, &multi) {
for _, e := range multi.Errors {
// Handle each error
}
}
}
Validation Integration ¶
Integrate external validators:
binder := binding.MustNew(
binding.WithValidator(myValidator),
)
Observability ¶
Add hooks for monitoring:
binder := binding.MustNew(
binding.WithEvents(binding.Events{
FieldBound: func(name, tag string) {
log.Printf("Bound field %s from %s", name, tag)
},
Done: func(stats binding.Stats) {
log.Printf("Bound %d fields", stats.FieldsBound)
},
}),
)
Security Limits ¶
Built-in limits prevent resource exhaustion:
- MaxDepth: Maximum struct nesting depth (default: 32)
- MaxSliceLen: Maximum slice elements (default: 10,000)
- MaxMapSize: Maximum map entries (default: 1,000)
Configure limits:
binding.MustNew(
binding.WithMaxDepth(16),
binding.WithMaxSliceLen(1000),
binding.WithMaxMapSize(500),
)
Index ¶
- Constants
- Variables
- func AssertNoBindError(t *testing.T, err error)
- func Bind[T any](opts ...Option) (T, error)
- func BindTo(out any, opts ...Option) error
- func BindWith[T any](b *Binder, opts ...Option) (T, error)
- func Cookie[T any](cookies []*http.Cookie, opts ...Option) (T, error)
- func CookieTo(cookies []*http.Cookie, out any, opts ...Option) error
- func CookieWith[T any](b *Binder, cookies []*http.Cookie) (T, error)
- func Form[T any](values url.Values, opts ...Option) (T, error)
- func FormTo(values url.Values, out any, opts ...Option) error
- func FormWith[T any](b *Binder, values url.Values) (T, error)
- func HasStructTag(t reflect.Type, tag string) bool
- func Header[T any](h http.Header, opts ...Option) (T, error)
- func HeaderTo(h http.Header, out any, opts ...Option) error
- func HeaderWith[T any](b *Binder, h http.Header) (T, error)
- func JSON[T any](body []byte, opts ...Option) (T, error)
- func JSONReader[T any](r io.Reader, opts ...Option) (T, error)
- func JSONReaderTo(r io.Reader, out any, opts ...Option) error
- func JSONReaderWith[T any](b *Binder, r io.Reader) (T, error)
- func JSONTo(body []byte, out any, opts ...Option) error
- func JSONWith[T any](b *Binder, body []byte) (T, error)
- func MustBind[T any](t *testing.T, getter ValueGetter, tag string, opts ...Option) T
- func MustBindForm[T any](t *testing.T, values url.Values, opts ...Option) T
- func MustBindJSON[T any](t *testing.T, jsonData string, opts ...Option) T
- func MustBindQuery[T any](t *testing.T, values url.Values, opts ...Option) T
- func MustWarmupCache(types ...any)
- func Path[T any](params map[string]string, opts ...Option) (T, error)
- func PathTo(params map[string]string, out any, opts ...Option) error
- func PathWith[T any](b *Binder, params map[string]string) (T, error)
- func Query[T any](values url.Values, opts ...Option) (T, error)
- func QueryTo(values url.Values, out any, opts ...Option) error
- func QueryWith[T any](b *Binder, values url.Values) (T, error)
- func Raw(getter ValueGetter, tag string, out any, opts ...Option) error
- func RawInto[T any](getter ValueGetter, tag string, opts ...Option) (T, error)
- func WarmupCache(types ...any)
- func XML[T any](body []byte, opts ...Option) (T, error)
- func XMLReader[T any](r io.Reader, opts ...Option) (T, error)
- func XMLReaderTo(r io.Reader, out any, opts ...Option) error
- func XMLReaderWith[T any](b *Binder, r io.Reader) (T, error)
- func XMLTo(body []byte, out any, opts ...Option) error
- func XMLWith[T any](b *Binder, body []byte) (T, error)
- type BindError
- type Binder
- func (b *Binder) BindTo(out any, opts ...Option) error
- func (b *Binder) CookieTo(cookies []*http.Cookie, out any) error
- func (b *Binder) FormTo(values url.Values, out any) error
- func (b *Binder) HeaderTo(h http.Header, out any) error
- func (b *Binder) JSONReaderTo(r io.Reader, out any) error
- func (b *Binder) JSONTo(body []byte, out any) error
- func (b *Binder) PathTo(params map[string]string, out any) error
- func (b *Binder) QueryTo(values url.Values, out any) error
- func (b *Binder) XMLReaderTo(r io.Reader, out any) error
- func (b *Binder) XMLTo(body []byte, out any) error
- type CookieGetter
- type Events
- type FormGetter
- type GetterFunc
- type HeaderGetter
- type KeyNormalizer
- type Metadata
- type MultiError
- func (m *MultiError) Add(err *BindError)
- func (m *MultiError) Code() string
- func (m *MultiError) Details() any
- func (m *MultiError) Error() string
- func (m *MultiError) ErrorOrNil() error
- func (m *MultiError) HTTPStatus() int
- func (m *MultiError) HasErrors() bool
- func (m *MultiError) Unwrap() []error
- type Option
- func FromCookie(cookies []*http.Cookie) Option
- func FromForm(values url.Values) Option
- func FromGetter(getter ValueGetter, tag string) Option
- func FromHeader(h http.Header) Option
- func FromJSON(body []byte) Option
- func FromJSONReader(r io.Reader) Option
- func FromPath(params map[string]string) Option
- func FromQuery(values url.Values) Option
- func FromXML(body []byte) Option
- func FromXMLReader(r io.Reader) Option
- func WithAllErrors() Option
- func WithConverter[T any](fn func(string) (T, error)) Option
- func WithEvents(events Events) Option
- func WithIntBaseAuto() Option
- func WithJSONUseNumber() Option
- func WithKeyNormalizer(normalizer KeyNormalizer) Option
- func WithMaxDepth(depth int) Option
- func WithMaxMapSize(n int) Option
- func WithMaxSliceLen(n int) Option
- func WithRequired() Option
- func WithSliceMode(mode SliceParseMode) Option
- func WithTimeLayouts(layouts ...string) Option
- func WithTypeConverter(targetType reflect.Type, converter TypeConverter) Option
- func WithUnknownFields(policy UnknownFieldPolicy) Option
- func WithValidator(v Validator) Option
- func WithXMLStrict() Option
- type PathGetter
- type QueryGetter
- type SliceParseMode
- type Source
- type Stats
- type TestValidator
- type TypeConverter
- type UnknownFieldError
- type UnknownFieldPolicy
- type Validator
- type ValueGetter
- func TestCookieGetter(t *testing.T, pairs ...string) ValueGetter
- func TestFormGetter(t *testing.T, pairs ...string) ValueGetter
- func TestHeaderGetter(t *testing.T, pairs ...string) ValueGetter
- func TestPathGetter(t *testing.T, pairs ...string) ValueGetter
- func TestQueryGetter(t *testing.T, pairs ...string) ValueGetter
- func TestQueryGetterMulti(t *testing.T, values map[string][]string) ValueGetter
Examples ¶
Constants ¶
const ( // DefaultMaxDepth is the default maximum nesting depth for structs and maps. // It prevents stack overflow from malicious deeply-nested payloads. DefaultMaxDepth = 32 // DefaultMaxMapSize is the default maximum number of map entries per field. // It prevents resource exhaustion from large map bindings. DefaultMaxMapSize = 1000 // DefaultMaxSliceLen is the default maximum number of slice elements per field. // It prevents memory exhaustion from large slice bindings. DefaultMaxSliceLen = 10_000 // DefaultMaxBodySize is the default maximum request body size (10 MiB). // This limit is enforced at the router layer, not in the binding package. DefaultMaxBodySize = 10 << 20 )
Security and resilience limits for binding operations.
const ( TagJSON = "json" // JSON struct tag TagQuery = "query" // Query parameter struct tag TagPath = "path" // URL path parameter struct tag TagForm = "form" // Form data struct tag TagHeader = "header" // HTTP header struct tag TagCookie = "cookie" // Cookie struct tag TagXML = "xml" // XML struct tag TagYAML = "yaml" // YAML struct tag TagTOML = "toml" // TOML struct tag TagMsgPack = "msgpack" // MessagePack struct tag TagProto = "proto" // Protocol Buffers struct tag )
Tag name constants for struct tags used in binding.
Variables ¶
var ( ErrUnsupportedContentType = errors.New("unsupported content type") ErrRequestBodyNil = errors.New("request body is nil") ErrOutMustBePointer = errors.New("out must be a pointer to struct") ErrOutPointerNil = errors.New("out pointer is nil") ErrInvalidIPAddress = errors.New("invalid IP address") ErrUnsupportedType = errors.New("unsupported type") ErrInvalidBooleanValue = errors.New("invalid boolean value") ErrEmptyTimeValue = errors.New("empty time value") ErrUnableToParseTime = errors.New("unable to parse time") ErrOnlyMapStringTSupported = errors.New("only map[string]T is supported") ErrInvalidBracketNotation = errors.New("invalid bracket notation in key") ErrValueNotInAllowedValues = errors.New("value not in allowed values") ErrMaxDepthExceeded = errors.New("exceeded maximum nesting depth") ErrSliceExceedsMaxLength = errors.New("slice exceeds max length") ErrMapExceedsMaxSize = errors.New("map exceeds max size") ErrInvalidStructTag = errors.New("invalid struct tag") ErrInvalidUUIDFormat = errors.New("invalid UUID format") ErrRequiredField = errors.New("required field is missing") ErrNoSourcesProvided = errors.New("no binding sources provided") )
Static errors for binding operations.
Functions ¶
func AssertNoBindError ¶ added in v0.2.0
AssertNoBindError asserts that the error is nil. Provides a cleaner failure message than require.NoError for binding contexts.
Example:
err := binding.Raw(getter, binding.TagQuery, ¶ms) binding.AssertNoBindError(t, err)
func Bind ¶
Bind binds from one or more sources specified via From* options.
Example:
req, err := binding.Bind[CreateOrderRequest](
binding.FromPath(pathParams),
binding.FromQuery(r.URL.Query()),
binding.FromJSON(body),
binding.WithRequired(),
)
Errors:
- ErrNoSourcesProvided: no binding sources provided via From* options
- ErrOutMustBePointer: T is not a struct type
- ErrRequiredField: when WithRequired is used and a required field is missing
- ErrMaxDepthExceeded: struct nesting exceeds maximum depth
- UnknownFieldError: when WithUnknownFields is UnknownError and unknown fields are present
- BindError: field-level binding errors with detailed context
- MultiError: when WithAllErrors is used and multiple errors occur
Example ¶
ExampleBind demonstrates multi-source binding.
package main
import (
"fmt"
"net/url"
"rivaas.dev/binding"
)
func main() {
type Request struct {
// From path parameters
UserID int `path:"user_id"`
// From query string
Page int `query:"page"`
Limit int `query:"limit"`
}
pathParams := map[string]string{"user_id": "456"}
query := url.Values{}
query.Set("page", "2")
query.Set("limit", "20")
req, err := binding.Bind[Request](
binding.FromPath(pathParams),
binding.FromQuery(query),
)
if err != nil {
_, _ = fmt.Printf("Error: %v\n", err)
return
}
_, _ = fmt.Printf("UserID: %d, Page: %d, Limit: %d\n", req.UserID, req.Page, req.Limit)
}
Output: UserID: 456, Page: 2, Limit: 20
func BindTo ¶ added in v0.2.0
BindTo binds from one or more sources specified via From* options.
Example:
var req CreateOrderRequest
err := binding.BindTo(&req,
binding.FromPath(pathParams),
binding.FromQuery(r.URL.Query()),
binding.FromJSON(body),
)
Example ¶
ExampleBindTo demonstrates non-generic multi-source binding.
package main
import (
"fmt"
"net/url"
"rivaas.dev/binding"
)
func main() {
type Request struct {
UserID int `path:"user_id"`
Page int `query:"page"`
}
pathParams := map[string]string{"user_id": "789"}
query := url.Values{}
query.Set("page", "3")
var req Request
err := binding.BindTo(&req,
binding.FromPath(pathParams),
binding.FromQuery(query),
)
if err != nil {
_, _ = fmt.Printf("Error: %v\n", err)
return
}
_, _ = fmt.Printf("UserID: %d, Page: %d\n", req.UserID, req.Page)
}
Output: UserID: 789, Page: 3
func BindWith ¶ added in v0.2.0
BindWith binds from one or more sources to type T using the Binder's config.
Example:
req, err := binding.BindWith[CreateOrderRequest](binder,
binding.FromPath(pathParams),
binding.FromQuery(r.URL.Query()),
binding.FromJSON(body),
)
func Cookie ¶ added in v0.2.0
Cookie binds cookies to type T.
Example:
session, err := binding.Cookie[SessionData](r.Cookies())
Errors:
- ErrOutMustBePointer: T is not a struct type
- ErrRequiredField: when WithRequired is used and a required field is missing
- ErrMaxDepthExceeded: struct nesting exceeds maximum depth
- BindError: field-level binding errors with detailed context
func CookieTo ¶ added in v0.2.0
CookieTo binds cookies to out.
Example:
var session SessionData err := binding.CookieTo(r.Cookies(), &session)
func CookieWith ¶ added in v0.2.0
CookieWith binds cookies to type T using the Binder's config.
Example:
session, err := binding.CookieWith[SessionData](binder, r.Cookies())
func Form ¶ added in v0.2.0
Form binds form data to type T.
Example:
data, err := binding.Form[FormData](r.PostForm)
Errors:
- ErrOutMustBePointer: T is not a struct type
- ErrRequiredField: when WithRequired is used and a required field is missing
- ErrMaxDepthExceeded: struct nesting exceeds maximum depth
- ErrSliceExceedsMaxLength: slice length exceeds maximum
- ErrMapExceedsMaxSize: map size exceeds maximum
- BindError: field-level binding errors with detailed context
func FormTo ¶ added in v0.2.0
FormTo binds form data to out.
Example:
var data FormData err := binding.FormTo(r.PostForm, &data)
func FormWith ¶ added in v0.2.0
FormWith binds form data to type T using the Binder's config.
Example:
data, err := binding.FormWith[FormData](binder, r.PostForm)
func HasStructTag ¶
HasStructTag checks if any field in the struct has the given tag. It recursively checks embedded structs to determine if the tag is present anywhere in the type hierarchy.
This is useful for determining which binding sources should be used when binding from multiple sources with Bind or BindTo.
Parameters:
Returns true if any field (including in embedded structs) has the tag.
Example ¶
ExampleHasStructTag demonstrates checking if a struct has specific tags.
package main
import (
"fmt"
)
func main() {
type UserRequest struct {
ID int `path:"id"`
Name string `query:"name"`
Auth string `header:"Authorization"`
}
// Check at compile time which sources a struct uses
var req UserRequest
_ = req // Use the variable
// In real code, you'd use reflect.TypeOf:
// typ := reflect.TypeOf((*UserRequest)(nil)).Elem()
// hasPath := binding.HasStructTag(typ, binding.TagPath)
_, _ = fmt.Printf("UserRequest has multiple source tags\n")
}
Output: UserRequest has multiple source tags
func Header ¶ added in v0.2.0
Header binds HTTP headers to type T.
Example:
headers, err := binding.Header[RequestHeaders](r.Header)
func HeaderTo ¶ added in v0.2.0
HeaderTo binds HTTP headers to out.
Example:
var headers RequestHeaders err := binding.HeaderTo(r.Header, &headers)
func HeaderWith ¶ added in v0.2.0
HeaderWith binds HTTP headers to type T using the Binder's config.
Example:
headers, err := binding.HeaderWith[RequestHeaders](binder, r.Header)
func JSON ¶ added in v0.2.0
JSON binds JSON bytes to type T.
Example:
user, err := binding.JSON[CreateUserRequest](body)
// With options
user, err := binding.JSON[CreateUserRequest](body,
binding.WithUnknownFields(binding.UnknownError),
binding.WithRequired(),
)
Errors:
- ErrOutMustBePointer: T is not a struct type
- ErrRequiredField: when WithRequired is used and a required field is missing
- ErrMaxDepthExceeded: struct nesting exceeds maximum depth
- UnknownFieldError: when WithUnknownFields is UnknownError and unknown fields are present
- BindError: field-level binding errors with detailed context
- MultiError: when WithAllErrors is used and multiple errors occur
Example ¶
ExampleJSON demonstrates binding from JSON body.
package main
import (
"fmt"
"rivaas.dev/binding"
)
func main() {
type User struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
body := []byte(`{"name": "Charlie", "email": "charlie@example.com", "age": 35}`)
user, err := binding.JSON[User](body)
if err != nil {
_, _ = fmt.Printf("Error: %v\n", err)
return
}
_, _ = fmt.Printf("Name: %s, Email: %s, Age: %d\n", user.Name, user.Email, user.Age)
}
Output: Name: Charlie, Email: charlie@example.com, Age: 35
Example (WithUnknownFields) ¶
ExampleJSON_withUnknownFields demonstrates strict JSON binding.
package main
import (
"fmt"
"rivaas.dev/binding"
)
func main() {
type User struct {
Name string `json:"name"`
}
// JSON with unknown field "extra"
body := []byte(`{"name": "Eve", "extra": "ignored"}`)
// Default: unknown fields are ignored
user, err := binding.JSON[User](body)
if err != nil {
_, _ = fmt.Printf("Error: %v\n", err)
return
}
_, _ = fmt.Printf("Name: %s\n", user.Name)
}
Output: Name: Eve
func JSONReader ¶ added in v0.2.0
JSONReader binds JSON from an io.Reader to type T.
Example:
user, err := binding.JSONReader[CreateUserRequest](r.Body)
Errors:
- ErrOutMustBePointer: T is not a struct type
- ErrRequiredField: when WithRequired is used and a required field is missing
- ErrMaxDepthExceeded: struct nesting exceeds maximum depth
- UnknownFieldError: when WithUnknownFields is UnknownError and unknown fields are present
- BindError: field-level binding errors with detailed context
func JSONReaderTo ¶ added in v0.2.0
JSONReaderTo binds JSON from an io.Reader to out.
Example:
var user CreateUserRequest err := binding.JSONReaderTo(r.Body, &user)
func JSONReaderWith ¶ added in v0.2.0
JSONReaderWith binds JSON from an io.Reader to type T using the Binder's config.
Example:
user, err := binding.JSONReaderWith[CreateUserRequest](binder, r.Body)
func JSONTo ¶ added in v0.2.0
JSONTo binds JSON bytes to out.
Example:
var user CreateUserRequest err := binding.JSONTo(body, &user)
func JSONWith ¶ added in v0.2.0
JSONWith binds JSON bytes to type T using the Binder's config.
Example:
user, err := binding.JSONWith[CreateUserRequest](binder, body)
func MustBind ¶ added in v0.2.0
MustBind is a test helper that binds and fails the test if binding fails. It's useful when binding must succeed for the test to proceed.
Example:
params := binding.MustBind[SearchParams](t, getter, binding.TagQuery)
func MustBindForm ¶ added in v0.2.0
MustBindForm is a test helper for form binding that fails if binding fails.
Example:
data := binding.MustBindForm[FormData](t, url.Values{"username": {"test"}})
func MustBindJSON ¶ added in v0.2.0
MustBindJSON is a test helper for JSON binding that fails if binding fails.
Example:
user := binding.MustBindJSON[User](t, `{"name":"John","age":30}`)
func MustBindQuery ¶ added in v0.2.0
MustBindQuery is a test helper for query binding that fails if binding fails.
Example:
params := binding.MustBindQuery[SearchParams](t, url.Values{"q": {"golang"}})
func MustWarmupCache ¶
func MustWarmupCache(types ...any)
MustWarmupCache is like WarmupCache but panics on invalid types. Use during application startup to validate struct tags at startup.
Example:
func init() {
binding.MustWarmupCache(
UserRequest{},
SearchParams{},
)
}
Parameters:
- types: Variadic list of struct instances to warm up
Panics if any type is invalid or has invalid struct tags.
func Path ¶ added in v0.2.0
Path binds URL path parameters to type T.
Example:
params, err := binding.Path[GetUserParams](pathParams)
Example ¶
ExamplePath demonstrates binding from path parameters.
package main
import (
"fmt"
"rivaas.dev/binding"
)
func main() {
type Params struct {
ID int `path:"id"`
Slug string `path:"slug"`
}
pathParams := map[string]string{
"id": "123",
"slug": "hello-world",
}
params, err := binding.Path[Params](pathParams)
if err != nil {
_, _ = fmt.Printf("Error: %v\n", err)
return
}
_, _ = fmt.Printf("ID: %d, Slug: %s\n", params.ID, params.Slug)
}
Output: ID: 123, Slug: hello-world
func PathTo ¶ added in v0.2.0
PathTo binds URL path parameters to out.
Example:
var params GetUserParams err := binding.PathTo(pathParams, ¶ms)
func PathWith ¶ added in v0.2.0
PathWith binds URL path parameters to type T using the Binder's config.
Example:
params, err := binding.PathWith[GetUserParams](binder, pathParams)
func Query ¶ added in v0.2.0
Query binds URL query parameters to type T.
Example:
params, err := binding.Query[ListParams](r.URL.Query())
// With options
params, err := binding.Query[ListParams](r.URL.Query(),
binding.WithRequired(),
)
Errors:
- ErrOutMustBePointer: T is not a struct type
- ErrRequiredField: when WithRequired is used and a required field is missing
- ErrMaxDepthExceeded: struct nesting exceeds maximum depth
- ErrSliceExceedsMaxLength: slice length exceeds maximum
- ErrMapExceedsMaxSize: map size exceeds maximum
- BindError: field-level binding errors with detailed context
Example ¶
ExampleQuery demonstrates basic binding from query parameters using generic API.
package main
import (
"fmt"
"net/url"
"rivaas.dev/binding"
)
func main() {
type Params struct {
Name string `query:"name"`
Age int `query:"age"`
Email string `query:"email"`
}
values := url.Values{}
values.Set("name", "Alice")
values.Set("age", "30")
values.Set("email", "alice@example.com")
params, err := binding.Query[Params](values)
if err != nil {
_, _ = fmt.Printf("Error: %v\n", err)
return
}
_, _ = fmt.Printf("Name: %s, Age: %d, Email: %s\n", params.Name, params.Age, params.Email)
}
Output: Name: Alice, Age: 30, Email: alice@example.com
Example (WithDefaults) ¶
ExampleQuery_withDefaults demonstrates binding with default values.
package main
import (
"fmt"
"net/url"
"rivaas.dev/binding"
)
func main() {
type Config struct {
Port int `query:"port" default:"8080"`
Host string `query:"host" default:"localhost"`
Debug bool `query:"debug" default:"false"`
LogLevel string `query:"log_level" default:"info"`
}
// Empty query string - defaults will be applied
values := url.Values{}
config, err := binding.Query[Config](values)
if err != nil {
_, _ = fmt.Printf("Error: %v\n", err)
return
}
_, _ = fmt.Printf("Port: %d, Host: %s, Debug: %v, LogLevel: %s\n", config.Port, config.Host, config.Debug, config.LogLevel)
}
Output: Port: 8080, Host: localhost, Debug: false, LogLevel: info
Example (WithOptions) ¶
ExampleQuery_withOptions demonstrates binding with custom options.
package main
import (
"fmt"
"net/url"
"rivaas.dev/binding"
)
func main() {
type Params struct {
Tags []string `query:"tags"`
}
values := url.Values{}
values.Set("tags", "go,rust,python")
// Use CSV mode for comma-separated values
params, err := binding.Query[Params](values,
binding.WithSliceMode(binding.SliceCSV),
)
if err != nil {
_, _ = fmt.Printf("Error: %v\n", err)
return
}
_, _ = fmt.Printf("Tags: %v\n", params.Tags)
}
Output: Tags: [go rust python]
func QueryTo ¶ added in v0.2.0
QueryTo binds URL query parameters to out.
Example:
var params ListParams err := binding.QueryTo(r.URL.Query(), ¶ms)
Example ¶
ExampleQueryTo demonstrates non-generic query binding.
package main
import (
"fmt"
"net/url"
"rivaas.dev/binding"
)
func main() {
type Params struct {
Name string `query:"name"`
Age int `query:"age"`
Email string `query:"email"`
}
values := url.Values{}
values.Set("name", "Bob")
values.Set("age", "25")
values.Set("email", "bob@example.com")
var params Params
err := binding.QueryTo(values, ¶ms)
if err != nil {
_, _ = fmt.Printf("Error: %v\n", err)
return
}
_, _ = fmt.Printf("Name: %s, Age: %d, Email: %s\n", params.Name, params.Age, params.Email)
}
Output: Name: Bob, Age: 25, Email: bob@example.com
func QueryWith ¶ added in v0.2.0
QueryWith binds URL query parameters to type T using the Binder's config.
Example:
params, err := binding.QueryWith[ListParams](binder, r.URL.Query())
func Raw ¶ added in v0.2.0
func Raw(getter ValueGetter, tag string, out any, opts ...Option) error
Raw binds values from a ValueGetter to out using the specified tag. This is the low-level binding function for custom sources.
For built-in sources, prefer the type-safe functions: Query, Path, Form, etc.
Example:
customGetter := &MyCustomGetter{...}
err := binding.Raw(customGetter, "custom", &result)
Errors:
- ErrOutMustBePointer: out is not a pointer to struct
- ErrRequiredField: when WithRequired is used and a required field is missing
- ErrMaxDepthExceeded: struct nesting exceeds maximum depth
- BindError: field-level binding errors with detailed context
func RawInto ¶ added in v0.2.0
func RawInto[T any](getter ValueGetter, tag string, opts ...Option) (T, error)
RawInto binds values from a ValueGetter to type T using the specified tag. This is the generic low-level binding function for custom sources.
Example:
result, err := binding.RawInto[MyType](customGetter, "custom")
Errors:
- ErrOutMustBePointer: T is not a struct type
- ErrRequiredField: when WithRequired is used and a required field is missing
- ErrMaxDepthExceeded: struct nesting exceeds maximum depth
- BindError: field-level binding errors with detailed context
func WarmupCache ¶
func WarmupCache(types ...any)
WarmupCache pre-parses struct types to populate the type cache. Call this during application startup after defining your structs to populate the cache for known request types.
Invalid types are silently skipped. Use MustWarmupCache to panic on errors.
Example:
type UserRequest struct { ... }
type SearchParams struct { ... }
binding.WarmupCache(
UserRequest{},
SearchParams{},
)
Parameters:
- types: Variadic list of struct instances to warm up
func XML ¶ added in v0.2.0
XML binds XML bytes to type T.
Example:
user, err := binding.XML[CreateUserRequest](body)
// With options
user, err := binding.XML[CreateUserRequest](body,
binding.WithRequired(),
)
Errors:
- ErrOutMustBePointer: T is not a struct type
- ErrRequiredField: when WithRequired is used and a required field is missing
- ErrMaxDepthExceeded: struct nesting exceeds maximum depth
- BindError: field-level binding errors with detailed context
func XMLReader ¶ added in v0.2.0
XMLReader binds XML from an io.Reader to type T.
Example:
user, err := binding.XMLReader[CreateUserRequest](r.Body)
Errors:
- ErrOutMustBePointer: T is not a struct type
- ErrRequiredField: when WithRequired is used and a required field is missing
- ErrMaxDepthExceeded: struct nesting exceeds maximum depth
- BindError: field-level binding errors with detailed context
func XMLReaderTo ¶ added in v0.2.0
XMLReaderTo binds XML from an io.Reader to out.
Example:
var user CreateUserRequest err := binding.XMLReaderTo(r.Body, &user)
func XMLReaderWith ¶ added in v0.2.0
XMLReaderWith binds XML from an io.Reader to type T using the Binder's config.
Example:
user, err := binding.XMLReaderWith[CreateUserRequest](binder, r.Body)
Types ¶
type BindError ¶
type BindError struct {
Field string // Field name that failed binding
Source Source // Binding source (typed)
Value string // The value that failed conversion
Type reflect.Type // Expected Go type
Reason string // Human-readable reason for failure
Err error // Underlying error
}
BindError represents a binding error with field-level context. It provides detailed information about which field failed, what value was provided, and what type was expected.
Use errors.As to check for BindError:
var bindErr *BindError
if errors.As(err, &bindErr) {
fmt.Printf("Field: %s, Source: %s\n", bindErr.Field, bindErr.Source)
}
func AssertBindError ¶ added in v0.2.0
AssertBindError checks if an error is a BindError with the expected field name. Returns the BindError if found, fails the test otherwise.
Example:
err := binding.Raw(getter, binding.TagQuery, ¶ms) bindErr := binding.AssertBindError(t, err, "Age") assert.Equal(t, binding.SourceQuery, bindErr.Source)
func (*BindError) HTTPStatus ¶
HTTPStatus implements rivaas.dev/errors.ErrorType.
func (*BindError) IsEnum ¶ added in v0.2.0
IsEnum returns true if the error is due to an invalid enum value.
func (*BindError) IsRequired ¶ added in v0.2.0
IsRequired returns true if the error is due to a missing required field.
func (*BindError) IsType ¶ added in v0.2.0
IsType returns true if the error is due to a type conversion failure.
func (*BindError) IsValidation ¶ added in v0.2.0
IsValidation returns true if the error is a validation error.
type Binder ¶ added in v0.2.0
type Binder struct {
// contains filtered or unexported fields
}
Binder provides request data binding with configurable options.
Use New or MustNew to create a configured Binder, or use package-level functions (Query, JSON, etc.) for zero-configuration binding.
Binder is safe for concurrent use by multiple goroutines.
Note: Due to Go language limitations, generic methods are not supported. Use the generic helper functions QueryWith, JSONWith, etc. for generic binding with a Binder, or use the non-generic methods directly.
Example:
binder := binding.MustNew(
binding.WithConverter[uuid.UUID](uuid.Parse),
binding.WithTimeLayouts("2006-01-02"),
binding.WithRequired(),
)
// Generic usage with helper function
user, err := binding.JSONWith[CreateUserRequest](binder, body)
// Non-generic usage
var user CreateUserRequest
err := binder.JSONTo(body, &user)
func MustNew ¶ added in v0.2.0
MustNew creates a Binder with the given options. Panics if configuration is invalid.
Use in main() or init() where panic on startup is acceptable.
Example:
binder := binding.MustNew(
binding.WithConverter[uuid.UUID](uuid.Parse),
binding.WithTimeLayouts("2006-01-02"),
)
Example ¶
ExampleMustNew demonstrates creating a reusable Binder.
package main
import (
"fmt"
"rivaas.dev/binding"
)
func main() {
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
// Create a configured binder
binder := binding.MustNew(
binding.WithMaxDepth(16),
)
body := []byte(`{"name": "Diana", "email": "diana@example.com"}`)
// Use generic helper function with binder
user, err := binding.JSONWith[User](binder, body)
if err != nil {
_, _ = fmt.Printf("Error: %v\n", err)
return
}
_, _ = fmt.Printf("Name: %s, Email: %s\n", user.Name, user.Email)
}
Output: Name: Diana, Email: diana@example.com
func New ¶ added in v0.2.0
New creates a Binder with the given options. Returns an error if configuration is invalid.
Example:
binder, err := binding.New(
binding.WithMaxDepth(16),
binding.WithRequired(),
)
if err != nil {
return fmt.Errorf("failed to create binder: %w", err)
}
func TestBinder ¶ added in v0.2.0
TestBinder creates a Binder configured for testing. It uses sensible defaults that are appropriate for test scenarios.
Example:
func TestMyFeature(t *testing.T) {
binder := binding.TestBinder(t)
// use binder in test
}
func (*Binder) BindTo ¶ added in v0.2.0
BindTo binds from one or more sources specified via From* options.
Example:
var req CreateOrderRequest
err := binder.BindTo(&req,
binding.FromPath(pathParams),
binding.FromQuery(r.URL.Query()),
binding.FromJSON(body),
)
func (*Binder) CookieTo ¶ added in v0.2.0
CookieTo binds cookies to out.
Example:
var session SessionData err := binder.CookieTo(r.Cookies(), &session)
func (*Binder) FormTo ¶ added in v0.2.0
FormTo binds form data to out.
Example:
var data FormData err := binder.FormTo(r.PostForm, &data)
func (*Binder) HeaderTo ¶ added in v0.2.0
HeaderTo binds HTTP headers to out.
Example:
var headers RequestHeaders err := binder.HeaderTo(r.Header, &headers)
func (*Binder) JSONReaderTo ¶ added in v0.2.0
JSONReaderTo binds JSON from an io.Reader to out.
Example:
var user CreateUserRequest err := binder.JSONReaderTo(r.Body, &user)
func (*Binder) JSONTo ¶ added in v0.2.0
JSONTo binds JSON bytes to out.
Example:
var user CreateUserRequest err := binder.JSONTo(body, &user)
func (*Binder) PathTo ¶ added in v0.2.0
PathTo binds URL path parameters to out.
Example:
var params GetUserParams err := binder.PathTo(pathParams, ¶ms)
func (*Binder) QueryTo ¶ added in v0.2.0
QueryTo binds URL query parameters to out.
Example:
var params ListParams err := binder.QueryTo(r.URL.Query(), ¶ms)
func (*Binder) XMLReaderTo ¶ added in v0.2.0
XMLReaderTo binds XML from an io.Reader to out.
Example:
var user CreateUserRequest err := binder.XMLReaderTo(r.Body, &user)
type CookieGetter ¶
type CookieGetter struct {
// contains filtered or unexported fields
}
CookieGetter implements ValueGetter for HTTP cookies. Cookie names are case-sensitive per HTTP standard.
func NewCookieGetter ¶
func NewCookieGetter(c []*http.Cookie) *CookieGetter
NewCookieGetter creates a CookieGetter from a slice of HTTP cookies.
Example:
getter := binding.NewCookieGetter(r.Cookies()) err := binding.Raw(getter, "cookie", &result)
func (*CookieGetter) Get ¶
func (cg *CookieGetter) Get(key string) string
Get returns the first cookie value for the key. Cookie values are automatically URL-unescaped. If unescaping fails, the raw cookie value is returned.
func (*CookieGetter) GetAll ¶
func (cg *CookieGetter) GetAll(key string) []string
GetAll returns all cookie values for the key.
func (*CookieGetter) Has ¶
func (cg *CookieGetter) Has(key string) bool
Has returns whether the key exists.
type Events ¶
type Events struct {
// FieldBound is called after successfully binding a field.
// name: struct field name, fromTag: source tag (query, json, etc.)
FieldBound func(name, fromTag string)
// UnknownField is called when an unknown field is encountered.
// Only triggered when UnknownFieldPolicy is UnknownWarn or UnknownError.
// path: dot-separated field path (e.g., "user.address.unknown")
UnknownField func(path string)
// Done is called at the end of binding with statistics.
// Always called, even on error (use defer).
Done func(stats Stats)
}
Events provides hooks for observability without coupling.
type FormGetter ¶
type FormGetter struct {
// contains filtered or unexported fields
}
FormGetter implements ValueGetter for form data.
func NewFormGetter ¶
func NewFormGetter(v url.Values) *FormGetter
NewFormGetter creates a FormGetter from url.Values.
Example:
getter := binding.NewFormGetter(r.PostForm) err := binding.Raw(getter, "form", &result)
func (*FormGetter) ApproxLen ¶
func (f *FormGetter) ApproxLen(prefix string) int
ApproxLen estimates the number of keys starting with the given prefix. It checks both dot notation (prefix.) and bracket notation (prefix[).
func (*FormGetter) Get ¶
func (f *FormGetter) Get(key string) string
Get returns the first value for the key.
func (*FormGetter) GetAll ¶
func (f *FormGetter) GetAll(key string) []string
GetAll returns all values for the key. It supports both repeated key patterns ("ids=1&ids=2") and bracket notation ("ids[]=1&ids[]=2").
func (*FormGetter) Has ¶
func (f *FormGetter) Has(key string) bool
Has returns whether the key exists.
type GetterFunc ¶
GetterFunc is a function adapter that implements ValueGetter. It allows using a function directly as a ValueGetter without creating a custom type.
Example:
getter := binding.GetterFunc(func(key string) ([]string, bool) {
if val, ok := myMap[key]; ok {
return []string{val}, true
}
return nil, false
})
err := binding.Raw(getter, "custom", &result)
func (GetterFunc) Get ¶
func (f GetterFunc) Get(key string) string
Get returns the first value for the key.
func (GetterFunc) GetAll ¶
func (f GetterFunc) GetAll(key string) []string
GetAll returns all values for the key.
func (GetterFunc) Has ¶
func (f GetterFunc) Has(key string) bool
Has returns whether the key exists.
type HeaderGetter ¶
type HeaderGetter struct {
// contains filtered or unexported fields
}
HeaderGetter implements ValueGetter for HTTP headers. Headers are case-insensitive per HTTP standard, and keys are canonicalized using http.CanonicalHeaderKey.
func NewHeaderGetter ¶
func NewHeaderGetter(h http.Header) *HeaderGetter
NewHeaderGetter creates a HeaderGetter from http.Header. Header keys are normalized to canonical MIME header format for consistent lookups.
Example:
getter := binding.NewHeaderGetter(r.Header) err := binding.Raw(getter, "header", &result)
func (*HeaderGetter) Get ¶
func (h *HeaderGetter) Get(key string) string
Get returns the first header value for the key. Lookups are case-insensitive and use canonical header key format.
func (*HeaderGetter) GetAll ¶
func (h *HeaderGetter) GetAll(key string) []string
GetAll returns all header values for the key.
func (*HeaderGetter) Has ¶
func (h *HeaderGetter) Has(key string) bool
Has returns whether the key exists.
type KeyNormalizer ¶
KeyNormalizer transforms keys before lookup. Common uses include case-folding and canonicalization.
var ( // CanonicalMIME normalizes HTTP header keys (Content-Type -> Content-Type) CanonicalMIME KeyNormalizer = http.CanonicalHeaderKey // LowerCase converts keys to lowercase (case-insensitive matching) LowerCase KeyNormalizer = strings.ToLower )
Common normalizers
type Metadata ¶
type Metadata struct {
BodyRead bool // Whether the request body has been read
RawBody []byte // Cached raw body bytes
}
Metadata tracks binding state for framework integration. It is used by router.Context to cache body reads and presence maps.
type MultiError ¶
type MultiError struct {
Errors []*BindError
}
MultiError aggregates multiple binding errors. It is returned when WithAllErrors is used and multiple fields fail binding.
Use errors.As to check for MultiError:
var multi *MultiError
if errors.As(err, &multi) {
for _, e := range multi.Errors {
// Handle each error
}
}
func (*MultiError) Add ¶ added in v0.2.0
func (m *MultiError) Add(err *BindError)
Add appends an error to the MultiError.
func (*MultiError) Code ¶
func (m *MultiError) Code() string
Code implements rivaas.dev/errors.ErrorCode.
func (*MultiError) Details ¶
func (m *MultiError) Details() any
Details implements rivaas.dev/errors.ErrorDetails.
func (*MultiError) Error ¶
func (m *MultiError) Error() string
Error returns a formatted error message.
func (*MultiError) ErrorOrNil ¶ added in v0.2.0
func (m *MultiError) ErrorOrNil() error
ErrorOrNil returns nil if there are no errors, otherwise returns the MultiError.
func (*MultiError) HTTPStatus ¶
func (m *MultiError) HTTPStatus() int
HTTPStatus implements rivaas.dev/errors.ErrorType.
func (*MultiError) HasErrors ¶ added in v0.2.0
func (m *MultiError) HasErrors() bool
HasErrors returns true if there are any errors.
func (*MultiError) Unwrap ¶
func (m *MultiError) Unwrap() []error
Unwrap returns all errors for errors.Is/As compatibility.
type Option ¶
type Option func(*config)
Option configures binding behavior.
func FromCookie ¶ added in v0.2.0
FromCookie specifies cookies as a binding source for Bind or BindTo.
Example:
req, err := binding.Bind[Request](
binding.FromCookie(r.Cookies()),
)
func FromForm ¶ added in v0.2.0
FromForm specifies form data as a binding source for Bind or BindTo.
Example:
req, err := binding.Bind[Request](
binding.FromForm(r.PostForm),
)
func FromGetter ¶ added in v0.2.0
func FromGetter(getter ValueGetter, tag string) Option
FromGetter specifies a custom ValueGetter as a binding source. Use this for custom binding sources not covered by the built-in options.
Example:
customGetter := &MyCustomGetter{...}
req, err := binding.Bind[Request](
binding.FromGetter(customGetter, "custom"),
)
func FromHeader ¶ added in v0.2.0
FromHeader specifies HTTP headers as a binding source.
Example:
req, err := binding.Bind[Request](
binding.FromHeader(r.Header),
)
func FromJSON ¶ added in v0.2.0
FromJSON specifies JSON body as a binding source for Bind or BindTo. Note: JSON binding is handled separately from other sources.
Example:
req, err := binding.Bind[Request](
binding.FromQuery(r.URL.Query()),
binding.FromJSON(body),
)
func FromJSONReader ¶ added in v0.2.0
FromJSONReader specifies JSON from io.Reader as a binding source.
Example:
req, err := binding.Bind[Request](
binding.FromJSONReader(r.Body),
)
func FromPath ¶ added in v0.2.0
FromPath specifies path parameters as a binding source.
Example:
req, err := binding.Bind[Request](
binding.FromPath(pathParams),
)
func FromQuery ¶ added in v0.2.0
FromQuery specifies query parameters as a binding source for Bind or BindTo.
Example:
req, err := binding.Bind[Request](
binding.FromQuery(r.URL.Query()),
binding.FromPath(pathParams),
)
func FromXML ¶ added in v0.2.0
FromXML specifies XML body as a binding source.
Example:
req, err := binding.Bind[Request](
binding.FromQuery(r.URL.Query()),
binding.FromXML(body),
)
func FromXMLReader ¶ added in v0.2.0
FromXMLReader specifies XML from io.Reader as a binding source.
Example:
req, err := binding.Bind[Request](
binding.FromXMLReader(r.Body),
)
func WithAllErrors ¶ added in v0.2.0
func WithAllErrors() Option
WithAllErrors collects all binding errors instead of returning on first. When enabled, returns *MultiError containing all field errors.
Example:
user, err := binding.JSON[User](body, binding.WithAllErrors())
if err != nil {
var multi *binding.MultiError
if errors.As(err, &multi) {
for _, e := range multi.Errors {
// Handle each error
}
}
}
func WithConverter ¶ added in v0.2.0
WithConverter registers a custom type converter. Type-safe registration using generics.
Example:
binding.MustNew(
binding.WithConverter[uuid.UUID](uuid.Parse),
binding.WithConverter[decimal.Decimal](decimal.NewFromString),
)
func WithEvents ¶
WithEvents sets observability hooks.
Example:
binding.MustNew(binding.WithEvents(binding.Events{
FieldBound: func(name, tag string) {
log.Printf("Bound field %s from %s", name, tag)
},
Done: func(stats binding.Stats) {
log.Printf("Binding complete: %d fields", stats.FieldsBound)
},
}))
func WithIntBaseAuto ¶
func WithIntBaseAuto() Option
WithIntBaseAuto enables auto-detection of integer bases from prefixes. When enabled, recognizes 0x (hex), 0 (octal), and 0b (binary) prefixes.
Example:
binding.Query[T](values, binding.WithIntBaseAuto())
func WithJSONUseNumber ¶
func WithJSONUseNumber() Option
WithJSONUseNumber configures the JSON decoder to use json.Number instead of float64. This preserves numeric precision for large integers that would otherwise be represented as floats.
Example:
binding.JSON[T](body, binding.WithJSONUseNumber())
func WithKeyNormalizer ¶
func WithKeyNormalizer(normalizer KeyNormalizer) Option
WithKeyNormalizer sets a custom key normalization function.
Example:
binding.Header[T](h, binding.WithKeyNormalizer(binding.CanonicalMIME))
func WithMaxDepth ¶
WithMaxDepth sets the maximum nesting depth for structs and maps. When exceeded, binding returns ErrMaxDepthExceeded. The default is DefaultMaxDepth (32).
Example:
binding.JSON[T](body, binding.WithMaxDepth(16))
func WithMaxMapSize ¶
WithMaxMapSize sets the maximum number of map entries per field. When exceeded, binding returns ErrMapExceedsMaxSize. The default is DefaultMaxMapSize (1000). Set to 0 to disable the limit.
Example:
binding.Query[T](values, binding.WithMaxMapSize(500))
func WithMaxSliceLen ¶
WithMaxSliceLen sets the maximum number of slice elements per field. When exceeded, binding returns ErrSliceExceedsMaxLength. The default is DefaultMaxSliceLen (10,000). Set to 0 to disable the limit.
Example:
binding.Query[T](values, binding.WithMaxSliceLen(1000))
func WithRequired ¶ added in v0.2.0
func WithRequired() Option
WithRequired enables checking of `required` struct tags. When enabled, missing required fields return ErrRequiredField.
Example:
type User struct {
Name string `json:"name" required:"true"`
Email string `json:"email" required:"true"`
}
user, err := binding.JSON[User](body, binding.WithRequired())
func WithSliceMode ¶ added in v0.2.0
func WithSliceMode(mode SliceParseMode) Option
WithSliceMode sets how slice values are parsed from query/form data. SliceRepeat (default) expects repeated keys: ?tags=a&tags=b&tags=c SliceCSV expects comma-separated values: ?tags=a,b,c
Example:
binding.Query[T](values, binding.WithSliceMode(binding.SliceCSV))
func WithTimeLayouts ¶
WithTimeLayouts sets custom time parsing layouts. Default layouts are tried first, then custom layouts are attempted. Layouts use Go's time format reference time: Mon Jan 2 15:04:05 MST 2006.
Example:
binding.Query[T](values,
binding.WithTimeLayouts("2006-01-02", "01/02/2006"),
)
func WithTypeConverter ¶
func WithTypeConverter(targetType reflect.Type, converter TypeConverter) Option
WithTypeConverter registers a custom converter using reflect.Type. Use WithConverter[T] for type-safe registration when possible.
Example:
binding.MustNew(
binding.WithTypeConverter(
reflect.TypeFor[uuid.UUID](),
func(s string) (any, error) { return uuid.Parse(s) },
),
)
func WithUnknownFields ¶ added in v0.2.0
func WithUnknownFields(policy UnknownFieldPolicy) Option
WithUnknownFields sets how to handle unknown JSON fields. See UnknownFieldPolicy for available policies.
Example:
binding.JSON[T](body, binding.WithUnknownFields(binding.UnknownError))
func WithValidator ¶ added in v0.2.0
WithValidator integrates external validation via a Validator implementation. The validator is called after successful binding.
Example:
binding.MustNew(binding.WithValidator(myValidator))
func WithXMLStrict ¶ added in v0.2.0
func WithXMLStrict() Option
WithXMLStrict enables strict XML parsing mode. When enabled, the XML decoder will be more strict about element/attribute names.
Example:
binding.XML[T](body, binding.WithXMLStrict())
type PathGetter ¶ added in v0.2.0
type PathGetter struct {
// contains filtered or unexported fields
}
PathGetter implements ValueGetter for URL path parameters.
func NewPathGetter ¶ added in v0.2.0
func NewPathGetter(p map[string]string) *PathGetter
NewPathGetter creates a PathGetter from a map of path parameters.
Example:
getter := binding.NewPathGetter(map[string]string{"id": "123"})
func (*PathGetter) Get ¶ added in v0.2.0
func (p *PathGetter) Get(key string) string
Get returns the value for the key.
func (*PathGetter) GetAll ¶ added in v0.2.0
func (p *PathGetter) GetAll(key string) []string
GetAll returns all values for the key as a slice. Path parameters are single-valued, so this returns a slice with one element if the key exists.
func (*PathGetter) Has ¶ added in v0.2.0
func (p *PathGetter) Has(key string) bool
Has returns whether the key exists.
type QueryGetter ¶
type QueryGetter struct {
// contains filtered or unexported fields
}
QueryGetter implements ValueGetter for URL query parameters.
func NewQueryGetter ¶
func NewQueryGetter(v url.Values) *QueryGetter
NewQueryGetter creates a QueryGetter from url.Values.
Example:
getter := binding.NewQueryGetter(r.URL.Query()) err := binding.Raw(getter, "query", &result)
func (*QueryGetter) ApproxLen ¶
func (q *QueryGetter) ApproxLen(prefix string) int
ApproxLen estimates the number of keys starting with the given prefix. It checks both dot notation (prefix.) and bracket notation (prefix[).
func (*QueryGetter) Get ¶
func (q *QueryGetter) Get(key string) string
Get returns the first value for the key.
func (*QueryGetter) GetAll ¶
func (q *QueryGetter) GetAll(key string) []string
GetAll returns all values for the key. It supports both repeated key patterns ("ids=1&ids=2") and bracket notation ("ids[]=1&ids[]=2").
func (*QueryGetter) Has ¶
func (q *QueryGetter) Has(key string) bool
Has returns whether the key exists.
type SliceParseMode ¶
type SliceParseMode int
SliceParseMode defines how slice values are parsed from query/form data.
const ( SliceRepeat SliceParseMode = iota // ?tags=a&tags=b&tags=c (default) SliceCSV // ?tags=a,b,c )
type Source ¶ added in v0.2.0
type Source int
Source represents the binding source type.
const ( // SourceUnknown is an unspecified source. SourceUnknown Source = iota // SourceQuery represents URL query parameters. SourceQuery // SourcePath represents URL path parameters. SourcePath // SourceForm represents form data. SourceForm // SourceHeader represents HTTP headers. SourceHeader // SourceCookie represents HTTP cookies. SourceCookie // SourceJSON represents JSON body. SourceJSON // SourceXML represents XML body. SourceXML // SourceYAML represents YAML body. SourceYAML // SourceTOML represents TOML body. SourceTOML // SourceMsgPack represents MessagePack body. SourceMsgPack // SourceProto represents Protocol Buffers body. SourceProto )
type Stats ¶
type Stats struct {
FieldsProcessed int // Total fields attempted
FieldsBound int // Successfully bound fields
ErrorsEncountered int // Errors hit during binding
Duration time.Duration // Total binding time (if tracked externally)
}
Stats tracks binding operation metrics.
type TestValidator ¶ added in v0.2.0
TestValidator is a mock validator for testing validation integration.
func AlwaysFailValidator ¶ added in v0.2.0
func AlwaysFailValidator(msg string) *TestValidator
AlwaysFailValidator returns a validator that always returns an error. Useful for testing error handling paths.
Example:
validator := binding.AlwaysFailValidator("validation failed")
func NeverFailValidator ¶ added in v0.2.0
func NeverFailValidator() *TestValidator
NeverFailValidator returns a validator that never returns an error.
Example:
validator := binding.NeverFailValidator()
func NewTestValidator ¶ added in v0.2.0
func NewTestValidator(fn func(v any) error) *TestValidator
NewTestValidator creates a TestValidator with the given validation function.
Example:
validator := binding.NewTestValidator(func(v any) error {
user, ok := v.(*User)
if !ok {
return nil
}
if user.Age < 0 {
return errors.New("age must be non-negative")
}
return nil
})
func (*TestValidator) Validate ¶ added in v0.2.0
func (tv *TestValidator) Validate(v any) error
Validate implements the Validator interface.
type TypeConverter ¶
TypeConverter converts a string value to a custom type. Registered converters are checked before built-in type handling. If a converter returns an error, binding fails for that field.
type UnknownFieldError ¶
type UnknownFieldError struct {
Fields []string // List of unknown field names
}
UnknownFieldError is returned when strict JSON decoding encounters unknown fields. It contains the list of field names that were present in the JSON but not defined in the target struct.
func (*UnknownFieldError) Code ¶ added in v0.2.0
func (e *UnknownFieldError) Code() string
Code implements rivaas.dev/errors.ErrorCode.
func (*UnknownFieldError) Error ¶
func (e *UnknownFieldError) Error() string
Error returns a formatted error message.
func (*UnknownFieldError) HTTPStatus ¶ added in v0.2.0
func (e *UnknownFieldError) HTTPStatus() int
HTTPStatus implements rivaas.dev/errors.ErrorType.
type UnknownFieldPolicy ¶
type UnknownFieldPolicy int
UnknownFieldPolicy defines how to handle unknown fields during JSON decoding.
const ( // UnknownIgnore silently ignores unknown JSON fields. // This is the default policy. UnknownIgnore UnknownFieldPolicy = iota // UnknownWarn emits warnings via Events.UnknownField but continues binding. // It uses two-pass parsing to detect unknown fields at all nesting levels. // Recommended for development and testing environments. UnknownWarn // UnknownError returns an error on the first unknown field. // It uses json.Decoder.DisallowUnknownFields for strict validation. UnknownError )
type ValueGetter ¶
type ValueGetter interface {
// Get returns the first value for the given key, or an empty string if not present.
Get(key string) string
// GetAll returns all values for the given key, or nil if not present.
GetAll(key string) []string
// Has returns true if the key is present, even if its value is empty.
// This distinguishes "key present with empty value" from "key not present".
Has(key string) bool
}
ValueGetter abstracts different sources of input values for binding.
Implementers must distinguish between "key present with empty value" and "key not present". For example:
- Query string "?name=" → Has("name") = true, Get("name") = ""
- Query string "?foo=bar" → Has("name") = false
This distinction enables proper partial update semantics and default value application. The Has method should return true if the key exists in the source, even if its value is empty.
ValueGetter is the low-level interface for custom binding sources. For built-in sources, use the type-safe functions: Query, Path, Form, etc. Use Raw or RawInto to bind from a custom ValueGetter implementation.
func TestCookieGetter ¶ added in v0.2.0
func TestCookieGetter(t *testing.T, pairs ...string) ValueGetter
TestCookieGetter creates a CookieGetter from key-value pairs for testing.
Example:
getter := binding.TestCookieGetter(t, "session_id", "abc123", "theme", "dark")
func TestFormGetter ¶ added in v0.2.0
func TestFormGetter(t *testing.T, pairs ...string) ValueGetter
TestFormGetter creates a FormGetter from key-value pairs for testing.
Example:
getter := binding.TestFormGetter(t, "username", "testuser", "password", "secret")
func TestHeaderGetter ¶ added in v0.2.0
func TestHeaderGetter(t *testing.T, pairs ...string) ValueGetter
TestHeaderGetter creates a HeaderGetter from key-value pairs for testing.
Example:
getter := binding.TestHeaderGetter(t, "Authorization", "Bearer token", "X-Request-ID", "123")
func TestPathGetter ¶ added in v0.2.0
func TestPathGetter(t *testing.T, pairs ...string) ValueGetter
TestPathGetter creates a PathGetter from key-value pairs for testing.
Example:
getter := binding.TestPathGetter(t, "user_id", "123", "slug", "hello-world")
func TestQueryGetter ¶ added in v0.2.0
func TestQueryGetter(t *testing.T, pairs ...string) ValueGetter
TestQueryGetter creates a QueryGetter from key-value pairs for testing. It provides a more convenient way to create query parameters in tests.
Example:
getter := binding.TestQueryGetter(t, "name", "John", "age", "30")
func TestQueryGetterMulti ¶ added in v0.2.0
func TestQueryGetterMulti(t *testing.T, values map[string][]string) ValueGetter
TestQueryGetterMulti creates a QueryGetter that supports multiple values per key. Useful for testing slice bindings.
Example:
getter := binding.TestQueryGetterMulti(t, map[string][]string{
"tags": {"go", "rust", "python"},
"page": {"1"},
})
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package msgpack provides MessagePack binding support for the binding package.
|
Package msgpack provides MessagePack binding support for the binding package. |
|
Package proto provides Protocol Buffers binding support for the binding package.
|
Package proto provides Protocol Buffers binding support for the binding package. |
|
Package toml provides TOML binding support for the binding package.
|
Package toml provides TOML binding support for the binding package. |
|
Package yaml provides YAML binding support for the binding package.
|
Package yaml provides YAML binding support for the binding package. |