bind
API
bind
packageAPI reference for the bind
package.
Imports
(8)
F
function
TestBinder_QueryBinding
Parameters
t
pkg/bind/bind_test.go:8-27
func TestBinder_QueryBinding(t *testing.T)
{
req := httptest.NewRequest("GET", "/test?name=alice&age=30", nil)
type Target struct {
Name string `query:"name"`
Age int `query:"age"`
}
b := New().FromQuery(req)
var target Target
if err := b.Bind(&target); err != nil {
t.Fatalf("Bind: %v", err)
}
if target.Name != "alice" {
t.Errorf("Name = %q, want alice", target.Name)
}
if target.Age != 30 {
t.Errorf("Age = %d, want 30", target.Age)
}
}
F
function
TestBinder_HeaderBinding
Parameters
t
pkg/bind/bind_test.go:29-45
func TestBinder_HeaderBinding(t *testing.T)
{
req := httptest.NewRequest("GET", "/test", nil)
req.Header.Set("X-Request-ID", "abc-123")
type Target struct {
RequestID string `header:"X-Request-ID"`
}
b := New().FromHeader(req)
var target Target
if err := b.Bind(&target); err != nil {
t.Fatalf("Bind: %v", err)
}
if target.RequestID != "abc-123" {
t.Errorf("RequestID = %q, want abc-123", target.RequestID)
}
}
F
function
TestBinder_PathBinding
Parameters
t
pkg/bind/bind_test.go:47-62
func TestBinder_PathBinding(t *testing.T)
{
params := map[string]string{"id": "42"}
type Target struct {
ID int `path:"id"`
}
b := New().FromPath(func(key string) string { return params[key] })
var target Target
if err := b.Bind(&target); err != nil {
t.Fatalf("Bind: %v", err)
}
if target.ID != 42 {
t.Errorf("ID = %d, want 42", target.ID)
}
}
F
function
TestBinder_DefaultValues
Parameters
t
pkg/bind/bind_test.go:64-87
func TestBinder_DefaultValues(t *testing.T)
{
req := httptest.NewRequest("GET", "/test", nil)
type Target struct {
Page int `query:"page" default:"1"`
Verbose bool `query:"verbose" default:"true"`
Format string `default:"json"`
}
b := New().FromQuery(req)
var target Target
if err := b.Bind(&target); err != nil {
t.Fatalf("Bind: %v", err)
}
if target.Page != 1 {
t.Errorf("Page = %d, want 1", target.Page)
}
if !target.Verbose {
t.Error("Verbose = false, want true")
}
if target.Format != "json" {
t.Errorf("Format = %q, want json", target.Format)
}
}
F
function
TestBinder_QueryOverridesDefault
Parameters
t
pkg/bind/bind_test.go:89-104
func TestBinder_QueryOverridesDefault(t *testing.T)
{
req := httptest.NewRequest("GET", "/test?page=5", nil)
type Target struct {
Page int `query:"page" default:"1"`
}
b := New().FromQuery(req)
var target Target
if err := b.Bind(&target); err != nil {
t.Fatalf("Bind: %v", err)
}
if target.Page != 5 {
t.Errorf("Page = %d, want 5", target.Page)
}
}
F
function
TestBinder_BindJSON
Parameters
t
pkg/bind/bind_test.go:106-122
func TestBinder_BindJSON(t *testing.T)
{
type Inner struct {
Name string `json:"Name"`
}
type Target struct {
Body Inner `body:"json"`
}
var target Target
if err := New().BindJSON(&target, []byte(`{"Name":"bob"}`)); err != nil {
t.Fatalf("BindJSON: %v", err)
}
if target.Body.Name != "bob" {
t.Errorf("Name = %q, want bob", target.Body.Name)
}
}
F
function
TestBinder_AllSources
Parameters
t
pkg/bind/bind_test.go:124-157
func TestBinder_AllSources(t *testing.T)
{
req := httptest.NewRequest("GET", "/test?search=golang", nil)
req.Header.Set("X-Token", "secret")
params := map[string]string{"id": "99"}
type Target struct {
ID int `path:"id"`
Search string `query:"search"`
Token string `header:"X-Token"`
Limit int `default:"10"`
}
b := New().
FromPath(func(key string) string { return params[key] }).
FromQuery(req).
FromHeader(req)
var target Target
if err := b.Bind(&target); err != nil {
t.Fatalf("Bind: %v", err)
}
if target.ID != 99 {
t.Errorf("ID = %d, want 99", target.ID)
}
if target.Search != "golang" {
t.Errorf("Search = %q, want golang", target.Search)
}
if target.Token != "secret" {
t.Errorf("Token = %q, want secret", target.Token)
}
if target.Limit != 10 {
t.Errorf("Limit = %d, want 10", target.Limit)
}
}
T
type
SourceFunc
SourceFunc extracts a string value by key from a request source.
pkg/bind/bind.go:13-13
type SourceFunc func(key string) string
S
struct
Binder
Binder populates struct fields from path, query, header, and default sources.
pkg/bind/bind.go:16-18
type Binder struct
Methods
FromPath
Method
FromPath adds a path parameter source.
Parameters
fn
SourceFunc
Returns
func (*Binder) FromPath(fn SourceFunc) *Binder
{
b.sources = append(b.sources, source{tag: "path", fn: fn})
return b
}
FromQuery
Method
FromQuery adds a query parameter source using the request URL query.
Parameters
r
*http.Request
Returns
func (*Binder) FromQuery(r *http.Request) *Binder
{
b.sources = append(b.sources, source{tag: "query", fn: func(key string) string {
return r.URL.Query().Get(key)
}})
return b
}
FromHeader
Method
FromHeader adds an HTTP header source.
Parameters
r
*http.Request
Returns
func (*Binder) FromHeader(r *http.Request) *Binder
{
b.sources = append(b.sources, source{tag: "header", fn: func(key string) string {
return r.Header.Get(key)
}})
return b
}
FromFunc
Method
FromFunc adds a custom source with the given tag name.
Parameters
tag
string
fn
SourceFunc
Returns
func (*Binder) FromFunc(tag string, fn SourceFunc) *Binder
{
b.sources = append(b.sources, source{tag: tag, fn: fn})
return b
}
Bind
Method
Bind populates struct fields from all registered sources, then applies defaults.
Parameters
target
any
Returns
error
func (*Binder) Bind(target any) error
{
val := reflect.ValueOf(target)
if val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Struct {
return fmt.Errorf("bind: target must be a pointer to a struct")
}
elem := val.Elem()
typ := elem.Type()
values := make(map[int]string)
for _, src := range b.sources {
for i := 0; i < typ.NumField(); i++ {
if _, ok := values[i]; ok {
continue
}
fieldType := typ.Field(i)
tagVal, ok := fieldType.Tag.Lookup(src.tag)
if !ok || tagVal == "" {
continue
}
if v := src.fn(tagVal); v != "" {
values[i] = v
}
}
}
for i := 0; i < typ.NumField(); i++ {
if _, ok := values[i]; ok {
continue
}
fieldType := typ.Field(i)
if def, ok := fieldType.Tag.Lookup("default"); ok && def != "" {
values[i] = def
}
}
for idx, v := range values {
field := elem.Field(idx)
if !field.CanSet() {
continue
}
if err := setField(field, v); err != nil {
typeField := typ.Field(idx)
return fmt.Errorf("bind: field %s: %w", typeField.Name, err)
}
}
return nil
}
BindJSON
Method
BindJSON unmarshals data into the struct field tagged with body:"json".
Parameters
target
any
data
[]byte
Returns
error
func (*Binder) BindJSON(target any, data []byte) error
{
val := reflect.ValueOf(target)
if val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Struct {
return fmt.Errorf("bind: target must be a pointer to a struct")
}
elem := val.Elem()
typ := elem.Type()
for i := 0; i < elem.NumField(); i++ {
field := elem.Field(i)
fieldType := typ.Field(i)
if tag, ok := fieldType.Tag.Lookup("body"); ok && tag == "json" && field.CanSet() {
return json.Unmarshal(data, field.Addr().Interface())
}
}
return nil
}
Fields
| Name | Type | Description |
|---|---|---|
| sources | []source |
S
struct
source
pkg/bind/bind.go:20-23
type source struct
Fields
| Name | Type | Description |
|---|---|---|
| tag | string | |
| fn | SourceFunc |
Uses
F
function
New
New creates an empty Binder.
Returns
pkg/bind/bind.go:26-28
func New() *Binder
{
return &Binder{}
}
F
function
setField
Parameters
field
valStr
string
Returns
error
pkg/bind/bind.go:128-160
func setField(field reflect.Value, valStr string) error
{
switch field.Kind() {
case reflect.String:
field.SetString(valStr)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val, err := strconv.ParseInt(valStr, 10, 64)
if err != nil {
return err
}
field.SetInt(val)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
val, err := strconv.ParseUint(valStr, 10, 64)
if err != nil {
return err
}
field.SetUint(val)
case reflect.Bool:
val, err := strconv.ParseBool(valStr)
if err != nil {
return err
}
field.SetBool(val)
case reflect.Float32, reflect.Float64:
val, err := strconv.ParseFloat(valStr, 64)
if err != nil {
return err
}
field.SetFloat(val)
default:
return fmt.Errorf("unsupported type %s", field.Kind())
}
return nil
}
F
function
ioReadAll
ioReadAll reads the request body up to 1MB.
Parameters
r
Returns
[]byte
error
pkg/bind/bind.go:163-169
func ioReadAll(r *http.Request) ([]byte, error)
{
if r.Body == nil {
return nil, nil
}
defer r.Body.Close()
return io.ReadAll(io.LimitReader(r.Body, 1<<20))
}