mirror of https://github.com/gofiber/fiber.git
227 lines
5.1 KiB
Markdown
227 lines
5.1 KiB
Markdown
# 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:
|
|
|
|
```go
|
|
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:
|
|
|
|
```golang
|
|
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:
|
|
|
|
```golang
|
|
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.
|
|
|
|
```golang
|
|
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.
|
|
|
|
```go
|
|
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(...)`
|