fiber/docs/api/bind.md

16 KiB

id title description sidebar_position toc_max_heading_level
bind 📎 Bind Binds the request and response items to a struct. 4 4

Bindings are used to parse the request/response body, query parameters, cookies, and much more into a struct.

:::info All binder returned values are only valid within the handler. Do not store any references.
Make copies or use the Immutable setting instead. Read more... :::

Binders

Body

Binds the request body to a struct.

It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a JSON body with a field called Pass, you would use a struct field with json:"pass".

Content-Type Struct Tag
application/x-www-form-urlencoded form
multipart/form-data form
application/json json
application/xml xml
text/xml xml
func (b *Bind) Body(out any) error
type Person struct {
    Name string `json:"name" xml:"name" form:"name"`
    Pass string `json:"pass" xml:"pass" form:"pass"`
}

app.Post("/", func(c fiber.Ctx) error {
    p := new(Person)
    
    if err := c.Bind().Body(p); err != nil {
        return err
    }
    
    log.Println(p.Name) // john
    log.Println(p.Pass) // doe
    
    // ...
})

Run tests with the following curl commands:

# JSON
curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"john\",\"pass\":\"doe\"}" localhost:3000

# XML
curl -X POST -H "Content-Type: application/xml" --data "<login><name>john</name><pass>doe</pass></login>" localhost:3000

# Form URL-Encoded
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "name=john&pass=doe" localhost:3000

# Multipart Form
curl -X POST -F name=john -F pass=doe http://localhost:3000

Form

Binds the request or multipart form body data to a struct.

It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a form body with a field called Pass, you would use a struct field with form:"pass".

func (b *Bind) Form(out any) error
type Person struct {
    Name string `form:"name"`
    Pass string `form:"pass"`
}

app.Post("/", func(c fiber.Ctx) error {
    p := new(Person)
    
    if err := c.Bind().Form(p); err != nil {
        return err
    }
    
    log.Println(p.Name) // john
    log.Println(p.Pass) // doe
    
    // ...
})

Run tests with the following curl commands for both application/x-www-form-urlencoded and multipart/form-data:

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "name=john&pass=doe" localhost:3000
curl -X POST -H "Content-Type: multipart/form-data" -F "name=john" -F "pass=doe" localhost:3000

JSON

Binds the request JSON body to a struct.

It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a JSON body with a field called Pass, you would use a struct field with json:"pass".

func (b *Bind) JSON(out any) error
type Person struct {
    Name string `json:"name"`
    Pass string `json:"pass"`
}

app.Post("/", func(c fiber.Ctx) error {
    p := new(Person)
    
    if err := c.Bind().JSON(p); err != nil {
        return err
    }

    log.Println(p.Name) // john
    log.Println(p.Pass) // doe
    
    // ...
})

Run tests with the following curl command:

curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"john\",\"pass\":\"doe\"}" localhost:3000

XML

Binds the request XML body to a struct.

It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse an XML body with a field called Pass, you would use a struct field with xml:"pass".

func (b *Bind) XML(out any) error
// Field names should start with an uppercase letter
type Person struct {
    Name string `xml:"name"`
    Pass string `xml:"pass"`
}

app.Post("/", func(c fiber.Ctx) error {
    p := new(Person)
    
    if err := c.Bind().XML(p); err != nil {
        return err
    }
    
    log.Println(p.Name) // john
    log.Println(p.Pass) // doe
    
    // ...
})

Run tests with the following curl command:

curl -X POST -H "Content-Type: application/xml" --data "<login><name>john</name><pass>doe</pass></login>" localhost:3000

CBOR

Binds the request CBOR body to a struct.

It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a CBOR body with a field called Pass, you would use a struct field with cbor:"pass".

func (b *Bind) CBOR(out any) error
// Field names should start with an uppercase letter
type Person struct {
    Name string `cbor:"name"`
    Pass string `cbor:"pass"`
}

app.Post("/", func(c fiber.Ctx) error {
    p := new(Person)
    
    if err := c.Bind().CBOR(p); err != nil {
        return err
    }
    
    log.Println(p.Name) // john
    log.Println(p.Pass) // doe
    
    // ...
})

Run tests with the following curl command:

curl -X POST -H "Content-Type: application/cbor" --data "\xa2dnamedjohndpasscdoe" localhost:3000

This method is similar to Body Binding, but for cookie parameters.
It is important to use the struct tag cookie. For example, if you want to parse a cookie with a field called Age, you would use a struct field with cookie:"age".

func (b *Bind) Cookie(out any) error
type Person struct {
    Name string `cookie:"name"`
    Age  int    `cookie:"age"`
    Job  bool   `cookie:"job"`
}

app.Get("/", func(c fiber.Ctx) error {
    p := new(Person)
    
    if err := c.Bind().Cookie(p); err != nil {
        return err
    }
    
    log.Println(p.Name)  // Joseph
    log.Println(p.Age)   // 23
    log.Println(p.Job)   // true
})

Run tests with the following curl command:

curl --cookie "name=Joseph; age=23; job=true" http://localhost:8000/

Header

This method is similar to Body Binding, but for request headers.
It is important to use the struct tag header. For example, if you want to parse a request header with a field called Pass, you would use a struct field with header:"pass".

func (b *Bind) Header(out any) error
type Person struct {
    Name     string   `header:"name"`
    Pass     string   `header:"pass"`
    Products []string `header:"products"`
}

app.Get("/", func(c fiber.Ctx) error {
    p := new(Person)
    
    if err := c.Bind().Header(p); err != nil {
        return err
    }
    
    log.Println(p.Name)     // john
    log.Println(p.Pass)     // doe
    log.Println(p.Products) // [shoe hat]
    
    // ...
})

Run tests with the following curl command:

curl "http://localhost:3000/" -H "name: john" -H "pass: doe" -H "products: shoe,hat"

Query

This method is similar to Body Binding, but for query parameters.
It is important to use the struct tag query. For example, if you want to parse a query parameter with a field called Pass, you would use a struct field with query:"pass".

func (b *Bind) Query(out any) error
type Person struct {
    Name     string   `query:"name"`
    Pass     string   `query:"pass"`
    Products []string `query:"products"`
}

app.Get("/", func(c fiber.Ctx) error {
    p := new(Person)
    
    if err := c.Bind().Query(p); err != nil {
        return err
    }
    
    log.Println(p.Name)     // john
    log.Println(p.Pass)     // doe
    // Depending on fiber.Config{EnableSplittingOnParsers: false} - default
    log.Println(p.Products) // ["shoe,hat"]
    // With fiber.Config{EnableSplittingOnParsers: true}
    // log.Println(p.Products) // ["shoe", "hat"]
    
    // ...
})

Run tests with the following curl command:

curl "http://localhost:3000/?name=john&pass=doe&products=shoe,hat"

:::info For more parser settings, please refer to Config :::

RespHeader

This method is similar to Body Binding, but for response headers. It is important to use the struct tag respHeader. For example, if you want to parse a response header with a field called Pass, you would use a struct field with respHeader:"pass".

func (b *Bind) RespHeader(out any) error
type Person struct {
    Name     string   `respHeader:"name"`
    Pass     string   `respHeader:"pass"`
    Products []string `respHeader:"products"`
}

app.Get("/", func(c fiber.Ctx) error {
    p := new(Person)
    
    if err := c.Bind().RespHeader(p); err != nil {
        return err
    }
    
    log.Println(p.Name)     // john
    log.Println(p.Pass)     // doe
    log.Println(p.Products) // [shoe hat]
    
    // ...
})

Run tests with the following curl command:

curl "http://localhost:3000/" -H "name: john" -H "pass: doe" -H "products: shoe,hat"

URI

This method is similar to Body Binding, but for path parameters.
It is important to use the struct tag uri. For example, if you want to parse a path parameter with a field called Pass, you would use a struct field with uri:"pass".

func (b *Bind) URI(out any) error
// GET http://example.com/user/111
app.Get("/user/:id", func(c fiber.Ctx) error {
    param := struct {
        ID uint `uri:"id"`
    }{}
    
    if err := c.Bind().URI(&param); err != nil {
        return err
    }
    
    // ...
    return c.SendString(fmt.Sprintf("User ID: %d", param.ID))
})

Custom

To use custom binders, you have to use this method.

You can register them using the RegisterCustomBinder method of the Fiber instance.

func (b *Bind) Custom(name string, dest any) error
app := fiber.New()

// My custom binder
type customBinder struct{}

func (cb *customBinder) Name() string {
    return "custom"
}

func (cb *customBinder) MIMETypes() []string {
    return []string{"application/yaml"}
}

func (cb *customBinder) Parse(c fiber.Ctx, out any) error {
    // parse YAML body
    return yaml.Unmarshal(c.Body(), out)
}

// Register custom binder
app.RegisterCustomBinder(&customBinder{})

type User struct {
    Name string `yaml:"name"`
}

// curl -X POST http://localhost:3000/custom -H "Content-Type: application/yaml" -d "name: John"
app.Post("/custom", func(c fiber.Ctx) error {
    var user User
    // Use Custom binder by name
    if err := c.Bind().Custom("custom", &user); err != nil {
        return err
    }
    return c.JSON(user)
})

Internally, custom binders are also used in the Body method.
The MIMETypes method is used to check if the custom binder should be used for the given content type.

Options

For more control over error handling, you can use the following methods.

WithAutoHandling

If you want to handle binder errors automatically, you can use WithAutoHandling.
If there's an error, it will return the error and set HTTP status to 400 Bad Request. This function does NOT panic therefor you must still return on error explicitly

func (b *Bind) WithAutoHandling() *Bind

WithoutAutoHandling

To handle binder errors manually, you can use the WithoutAutoHandling method.
It's the default behavior of the binder.

func (b *Bind) WithoutAutoHandling() *Bind

SetParserDecoder

Allows you to configure the BodyParser/QueryParser decoder based on schema options, providing the possibility to add custom types for parsing.

func SetParserDecoder(parserConfig fiber.ParserConfig{
    IgnoreUnknownKeys bool,
    ParserType        []fiber.ParserType{
        Customtype any,
        Converter  func(string) reflect.Value,
    },
    ZeroEmpty         bool,
    SetAliasTag       string,
})

type CustomTime time.Time

// String returns the time in string format
func (ct *CustomTime) String() string {
    t := time.Time(*ct).String()
    return t
}

// Converter for CustomTime type with format "2006-01-02"
var timeConverter = func(value string) reflect.Value {
    fmt.Println("timeConverter:", value)
    if v, err := time.Parse("2006-01-02", value); err == nil {
        return reflect.ValueOf(CustomTime(v))
    }
    return reflect.Value{}
}

customTime := fiber.ParserType{
    CustomType: CustomTime{},
    Converter:  timeConverter,
}

// Add custom type to the Decoder settings
fiber.SetParserDecoder(fiber.ParserConfig{
    IgnoreUnknownKeys: true,
    ParserType:        []fiber.ParserType{customTime},
    ZeroEmpty:         true,
})

// Example using CustomTime with non-RFC3339 format
type Demo struct {
    Date  CustomTime `form:"date" query:"date"`
    Title string     `form:"title" query:"title"`
    Body  string     `form:"body" query:"body"`
}

app.Post("/body", func(c fiber.Ctx) error {
    var d Demo
    if err := c.Bind().Body(&d); err != nil {
        return err
    }
    fmt.Println("d.Date:", d.Date.String())
    return c.JSON(d)
})

app.Get("/query", func(c fiber.Ctx) error {
    var d Demo
    if err := c.Bind().Query(&d); err != nil {
        return err
    }
    fmt.Println("d.Date:", d.Date.String())
    return c.JSON(d)
})

// Run tests with the following curl commands:

# Body Binding
curl -X POST -F title=title -F body=body -F date=2021-10-20 http://localhost:3000/body

# Query Binding
curl -X GET "http://localhost:3000/query?title=title&body=body&date=2021-10-20"

Validation

Validation is also possible with the binding methods. You can specify your validation rules using the validate struct tag.

Specify your struct validator in the config.

Setup Your Validator in the Config

import "github.com/go-playground/validator/v10"

type structValidator struct {
    validate *validator.Validate
}

// Validate method implementation
func (v *structValidator) Validate(out any) error {
    return v.validate.Struct(out)
}

// Setup your validator in the Fiber config
app := fiber.New(fiber.Config{
    StructValidator: &structValidator{validate: validator.New()},
})

Usage of Validation in Binding Methods

type Person struct {
    Name string `json:"name" validate:"required"`
    Age  int    `json:"age" validate:"gte=18,lte=60"`
}

app.Post("/", func(c fiber.Ctx) error {
    p := new(Person)
    
    if err := c.Bind().JSON(p); err != nil { // Receives validation errors
        return err
    }
})

Default Fields

You can set default values for fields in the struct by using the default struct tag. Supported types:

  • bool
  • Float variants (float32, float64)
  • Int variants (int, int8, int16, int32, int64)
  • Uint variants (uint, uint8, uint16, uint32, uint64)
  • string
  • A slice of the above types. Use | to separate slice items.
  • A pointer to one of the above types (pointers to slices and slices of pointers are not supported).
type Person struct {
    Name     string     `query:"name,default:john"`
    Pass     string     `query:"pass"`
    Products []string   `query:"products,default:shoe|hat"`
}

app.Get("/", func(c fiber.Ctx) error {
    p := new(Person)

    if err := c.Bind().Query(p); err != nil {
        return err
    }

    log.Println(p.Name)     // john
    log.Println(p.Pass)     // doe
    log.Println(p.Products) // ["shoe", "hat"]
    
    // ...
})

Run tests with the following curl command:

curl "http://localhost:3000/?pass=doe"