5.1 KiB
Fiber Binders
Bind is new request/response binding feature for Fiber. By against old Fiber parsers, it supports custom binder registration, struct validation with high performance and easy to use.
It's introduced in Fiber v3 and a replacement of:
- BodyParser
- ParamsParser
- GetReqHeaders
- GetRespHeaders
- AllParams
- QueryParser
- ReqHeaderParser
Guides
There are 2 kind of binder in fiber
- request info binder for basic request, info including query,header,param,respHeader,cookie.
- request body binder, parsing request body like XML or JSON.
underling fiber will call app.config.*Decoder
to parse request body, so you need to find parsing details in their own document.
Binding basic request info
Fiber supports binding basic request data into the struct:
all tags you can use are:
- respHeader
- header
- query
- param
- cookie
- form
- multipart
(binding for Request/Response header are case in-sensitive)
private and anonymous fields will be ignored.
basically, you can bind all type int8/int16...uint64/int/uint/float32/float64/string/bool
, you can also bind their slice for non param
source.
int
and uint
, float and bool
are parsed by strconv.ParseInt
, strconv.ParseUint
, strconv.ParseFloat
and strconv.ParseBool
, if binder failed to parse input string, a error will be returned by binder.
Quick Start:
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"time"
fiber "github.com/gofiber/fiber/v3"
)
type Req struct {
ID int `param:"id"`
Q int `query:"q"`
Likes []int `query:"likes"`
T time.Time `header:"x-time"` // by time.Time.UnmarshalText, will ben explained later
Token string `header:"x-auth"`
}
func main() {
app := fiber.New()
app.Get("/:id", func(c fiber.Ctx) error {
var req Req
if err := c.Bind().Req(&req).Err(); err != nil {
return err
}
return c.JSON(req)
})
req := httptest.NewRequest(http.MethodGet, "/1?&s=a,b,c&q=47&likes=1&likes=2", http.NoBody)
req.Header.Set("x-auth", "ttt")
req.Header.Set("x-time", "2022-08-08T08:11:39+08:00")
resp, err := app.Test(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(resp.StatusCode, string(b))
// Output: 200 {"ID":1,"S":["a","b","c"],"Q":47,"Likes":[1,2],"T":"2022-08-08T08:11:39+08:00","Token":"ttt"}
}
Defining Custom Binder
We support 2 types of Custom Binder
a encoding.TextUnmarshaler
with basic tag config.
like the time.Time
field in the previous example, if a field implement encoding.TextUnmarshaler
, it will be called to parse raw string we get from request's query/header/...
Example:
type Req struct {
Start time.Time `query:"start_time"` // by time.Time.UnmarshalText, will ben explained later
}
a fiber.Binder
interface.
You don't need to set a field tag and it's binding tag will be ignored.
type Binder interface {
UnmarshalFiberCtx(ctx fiber.Ctx) error
}
If your type implement fiber.Binder
, bind will pass current request Context to your and you can unmarshal the request info you need.
Example:
type MyBinder struct{}
func (e *MyBinder) UnmarshalFiberCtx(ctx fiber.Ctx) error {
...
}
type Req struct {
Data MyBinder
}
Parse Request Body
you can call Bind().JSON(v any)
/ Bind().XML(v any)
/ Bind().Form(v any)
/ Bind().Multipart(v any)
to unmarshal request Body.
use Bind().Strict()
to enable content-type checking.
package main
type Body struct {
ID int `json:"..."`
Q int `json:"..."`
Likes []int `json:"..."`
T time.Time `json:"..."`
Token string `json:"..."`
}
func main() {
app := fiber.New()
app.Get("/", func(c fiber.Ctx) error {
var data Body
if err := c.Bind().JSON(&data).Err(); err != nil {
return err
}
return c.JSON(data)
})
app.Get("/strict", func(c fiber.Ctx) error {
var data Body
if err := c.Bind().Strict().JSON(&data).Err(); err != nil {
return err
}
return c.JSON(data)
})
}
Bind With validation
Normally, bind
will only try to unmarshal data from request and pass it to request handler.
you can call .Validate()
to validate previous binding.
And you will need to set a validator in app Config, otherwise it will always return an error.
package main
type Validator struct{}
func (validator *Validator) Validate(v any) error {
return nil
}
func main() {
app := fiber.New(fiber.Config{
Validator: &Validator{},
})
app.Get("/:id", func(c fiber.Ctx) error {
var req struct{}
var body struct{}
if err := c.Bind().Req(&req).Validate(). // will validate &req
JSON(&body).Validate(). // will validate &body
Err(); err != nil {
return err
}
return nil
})
}
Chaining API
Binder is expected to be called in chaining, and will do no-op after first error.
If ctx.Bind().XML/JSON/Req/Validate/...
meet any error, all calling will be ignored,
and .Err()
will return the first error encountered.
For example, if ctx.Bind().Req(...).JSON(...).Err()
return a non-nil error in Req(...)
,
binder won't try to decode body as JSON and .Err()
will return error in Req(...)