From 96f83a527dd605a80b634044dc829ec56917c497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Wed, 17 Apr 2024 17:07:09 +0200 Subject: [PATCH] [v3-Maintenance]-Consolidate-and-Document-Core-Changes-in-v3 --- docs/core/api/bind.md | 497 +++++++++++++----- versioned_docs/version-v3.x/api/bind.md | 497 +++++++++++++----- versioned_sidebars/version-v3.x-sidebars.json | 4 +- 3 files changed, 762 insertions(+), 236 deletions(-) diff --git a/docs/core/api/bind.md b/docs/core/api/bind.md index 2804ef8dfc7..2d2a643e7b2 100644 --- a/docs/core/api/bind.md +++ b/docs/core/api/bind.md @@ -3,30 +3,33 @@ id: bind title: 📎 Bind description: Binds the request and response items to a struct. sidebar_position: 4 +toc_max_heading_level: 4 --- -:::caution +Bindings are used to parse the request/response body, query parameters, cookies and much more into a struct. + +:::info -Documentation is still in progress. +All binder returned value are only valid within the handler. Do not store any references. +Make copies or use the [**`Immutable`**](./ctx.md) setting instead. [Read more...](../#zero-allocation) ::: -Bindings are used to parse the request/response body, query parameters, cookies and much more into a struct. + +## Binders - [Body](#body) + - [Form](#form) + - [JSON](#json) + - [MultipartForm](#multipartform) + - [XML](#xml) - [Cookie](#cookie) - [Header](#header) -- [ParamsParser](#paramsparser) - [Query](#query) - [RespHeader](#respheader) -- [SetParserDecoder](#setparserdecoder) +- [URI](#uri) - -> _Returned value is only valid within the handler. Do not store any references. -> Make copies or use the_ [_**`Immutable`**_](./ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) - - -## Body +### Body Binds the request body to a struct. @@ -52,16 +55,16 @@ type Person struct { } 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 - - // ... + 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 @@ -77,103 +80,226 @@ app.Post("/", func(c fiber.Ctx) error { // curl -X POST "http://localhost:3000/?name=john&pass=doe" ``` -## Cookie -This method is similar to [Body-Binding](#body), 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 of `cookie:"age"`. +**The methods for the various bodies can also be used directly:** + +#### Form + +Binds the request form 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 Form body with a field called Pass, you would use a struct field of `form:"pass"`. + ```go title="Signature" -func (b *Bind) Cookie(out any) error +func (b *Bind) Form(out any) error ``` ```go title="Example" // Field names should start with an uppercase letter type Person struct { - Name string `cookie:"name"` - Age int `cookie:"age"` - Job bool `cookie:"job"` + Name string `form:"name"` + Pass string `form:"pass"` } -app.Get("/", func(c fiber.Ctx) error { - p := new(Person) +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 + + // ... +}) - if err := c.Bind().Cookie(p); err != nil { - return err - } +// Run tests with the following curl commands - log.Println(p.Name) // Joseph - log.Println(p.Age) // 23 - log.Println(p.Job) // true -}) -// Run tests with the following curl command -// curl.exe --cookie "name=Joseph; age=23; job=true" http://localhost:8000/ +// curl -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "name=john&pass=doe" localhost:3000 ``` +#### JSON -## Header +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 of `json:"pass"`. -This method is similar to [Body-Binding](#body), but for request headers. -It is important to use the struct tag "reqHeader". For example, if you want to parse a request header with a field called Pass, you would use a struct field of `reqHeader:"pass"`. ```go title="Signature" -func (b *Bind) Header(out any) error +func (b *Bind) JSON(out any) error ``` ```go title="Example" // Field names should start with an uppercase letter type Person struct { - Name string `reqHeader:"name"` - Pass string `reqHeader:"pass"` - Products []string `reqHeader:"products"` + Name string `json:"name"` + Pass string `json:"pass"` } -app.Get("/", func(c fiber.Ctx) error { - p := new(Person) +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 commands + +// curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"john\",\"pass\":\"doe\"}" localhost:3000 + +``` + +#### MultipartForm + +Binds the request multipart form 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 MultipartForm body with a field called Pass, you would use a struct field of `form:"pass"`. - 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] +```go title="Signature" +func (b *Bind) MultipartForm(out any) error +``` + +```go title="Example" +// Field names should start with an uppercase letter +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().MultipartForm(p); err != nil { + return err + } + + log.Println(p.Name) // john + log.Println(p.Pass) // doe + + // ... }) -// Run tests with the following curl command -// curl "http://localhost:3000/" -H "name: john" -H "pass: doe" -H "products: shoe,hat" +// Run tests with the following curl commands + +// curl -X POST -H "Content-Type: multipart/form-data" -F "name=john" -F "pass=doe" localhost:3000 + ``` +#### XML -## ParamsParser +Binds the request xml form body to a struct. -:::caution +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 XML body with a field called Pass, you would use a struct field of `xml:"pass"`. -Not finished yet. -::: +```go title="Signature" +func (b *Bind) XML(out any) error +``` + +```go title="Example" +// Field names should start with an uppercase letter +type Person struct { + Name string `xml:"name"` + Pass string `xml:"pass"` +} -This method is similar to [Body-Binding](#body), but for path parameters. It is important to use the struct tag "params". For example, if you want to parse a path parameter with a field called Pass, you would use a struct field of params:"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 commands + +// curl -X POST -H "Content-Type: application/xml" --data "johndoe" localhost:3000 +``` + + +### Cookie + +This method is similar to [Body-Binding](#body), 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 of `cookie:"age"`. ```go title="Signature" -func (b *Bind) Params(out any) error +func (b *Bind) Cookie(out any) error ``` ```go title="Example" -// GET http://example.com/user/111 -app.Get("/user/:id", func(c fiber.Ctx) error { - param := struct {ID uint `params:"id"`}{} +// Field names should start with an uppercase letter +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.exe --cookie "name=Joseph; age=23; job=true" http://localhost:8000/ +``` + + +### Header - c.ParamsParser(¶m) // "{"id": 111}" +This method is similar to [Body-Binding](#body), 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 of `header:"pass"`. + +```go title="Signature" +func (b *Bind) Header(out any) error +``` - // ... +```go title="Example" +// Field names should start with an uppercase letter +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 +### Query This method is similar to [Body-Binding](#body), 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 of `query:"pass"`. @@ -191,21 +317,21 @@ type Person struct { } 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 - // fiber.Config{EnableSplittingOnParsers: false} - default - log.Println(p.Products) // ["shoe,hat"] - // fiber.Config{EnableSplittingOnParsers: true} - // log.Println(p.Products) // ["shoe", "hat"] - - - // ... + p := new(Person) + + if err := c.Bind().Query(p); err != nil { + return err + } + + log.Println(p.Name) // john + log.Println(p.Pass) // doe + // fiber.Config{EnableSplittingOnParsers: false} - default + log.Println(p.Products) // ["shoe,hat"] + // fiber.Config{EnableSplittingOnParsers: true} + // log.Println(p.Products) // ["shoe", "hat"] + + + // ... }) // Run tests with the following curl command @@ -217,7 +343,7 @@ For more parser settings please look here [Config](fiber.md#config) ::: -## RespHeader +### RespHeader This method is similar to [Body-Binding](#body), but for response headers. It is important to use the struct tag "respHeader". For example, if you want to parse a request header with a field called Pass, you would use a struct field of `respHeader:"pass"`. @@ -235,21 +361,116 @@ type Person struct { } app.Get("/", func(c fiber.Ctx) error { - p := new(Person) + 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 + +:::caution + +Not finished yet. - 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] +This method is similar to [Body-Binding](#body), 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 of uri:"pass" - // ... +```go title="Signature" +func (b *Bind) URI(out any) error +``` + +```go title="Example" +// GET http://example.com/user/111 +app.Get("/user/:id", func(c fiber.Ctx) error { + param := struct {ID uint `uri:"id"`}{} + + c.Bind().URI(¶m) // "{"id": 111}" + + // ... }) -// Run tests with the following curl command -// curl "http://localhost:3000/" -H "name: john" -H "pass: doe" -H "products: shoe,hat" +``` + +## Custom + +To use custom binders, you have to use this method. + +You can register them from [RegisterCustomBinder](./app.md#registercustombinder) method of Fiber instance. + +```go title="Signature" +func (b *Bind) Custom(name string, dest any) error +``` + +```go title="Example" +app := fiber.New() + +// My custom binder +customBinder := &customBinder{} +// Name of custom binder, which will be used as Bind().Custom("name") +func (*customBinder) Name() string { + return "custom" +} +// Is used in the Body Bind method to check if the binder should be used for custom mime types +func (*customBinder) MIMETypes() []string { + return []string{"application/yaml"} +} +// Parse the body and bind it to the out interface +func (*customBinder) Parse(c Ctx, out any) error { + // parse yaml body + return yaml.Unmarshal(c.Body(), out) +} +// Register custom binder +app.RegisterCustomBinder(customBinder) + +// curl -X POST http://localhost:3000/custom -H "Content-Type: application/yaml" -d "name: John" +app.Post("/custom", func(c Ctx) error { + var user User + // output: {Name:John} + // Custom binder is used by the name + if err := c.Bind().Custom("custom", &user); err != nil { + return err + } + // ... + return c.JSON(user) +}) +``` + +Internally they are also used in the [Body](#body) method. +For this the MIMETypes method is used to check if the custom binder should be used for the given content type. + +## Options + +For more control over the error handling, you can use the following methods. + +### Must + +If you want to handle binder errors automatically, you can use Must. +If there's an error it'll return error and 400 as HTTP status. + +```go title="Signature" +func (b *Bind) Must() *Bind +``` + +### Should + +To handle binder errors manually, you can prefer Should method. +It's default behavior of binder. + +```go title="Signature" +func (b *Bind) Should() *Bind ``` @@ -259,13 +480,13 @@ Allow you to config BodyParser/QueryParser decoder, base on schema's options, pr ```go title="Signature" func SetParserDecoder(parserConfig fiber.ParserConfig{ - IgnoreUnknownKeys bool, - ParserType []fiber.ParserType{ - Customtype any, - Converter func(string) reflect.Value, - }, - ZeroEmpty bool, - SetAliasTag string, + IgnoreUnknownKeys bool, + ParserType []fiber.ParserType{ + Customtype any, + Converter func(string) reflect.Value, + }, + ZeroEmpty bool, + SetAliasTag string, }) ``` @@ -276,28 +497,28 @@ type CustomTime time.Time // String() returns the time in string func (ct *CustomTime) String() string { t := time.Time(*ct).String() - return t -} - -// Register the converter for CustomTime type format as 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(v) - } - return reflect.Value{} + return t + } + + // Register the converter for CustomTime type format as 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(v) + } + return reflect.Value{} } customTime := fiber.ParserType{ - Customtype: CustomTime{}, - Converter: timeConverter, + Customtype: CustomTime{}, + Converter: timeConverter, } // Add setting to the Decoder fiber.SetParserDecoder(fiber.ParserConfig{ - IgnoreUnknownKeys: true, - ParserType: []fiber.ParserType{customTime}, - ZeroEmpty: true, + IgnoreUnknownKeys: true, + ParserType: []fiber.ParserType{customTime}, + ZeroEmpty: true, }) // Example to use CustomType, you pause custom time format not in RFC3339 @@ -326,3 +547,45 @@ app.Get("/query", func(c fiber.Ctx) error { // 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](./fiber.md#config) + +Setup your validator in the config + +```go title="Example" +import "github.com/go-playground/validator/v10" + +type structValidator struct { + validate *validator.Validate +} + +// Validator needs to implement the Validate method +func (v *structValidator) Validate(out any) error { + return v.validate.Struct(out) +} + +// Setup your validator in the config +app := fiber.New(fiber.Config{ + StructValidator: &structValidator{validate: validator.New()}, +}) +``` + +```go title="Example" +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 {// <- here you receive the validation errors + return err + } +}) +``` diff --git a/versioned_docs/version-v3.x/api/bind.md b/versioned_docs/version-v3.x/api/bind.md index 2804ef8dfc7..2d2a643e7b2 100644 --- a/versioned_docs/version-v3.x/api/bind.md +++ b/versioned_docs/version-v3.x/api/bind.md @@ -3,30 +3,33 @@ id: bind title: 📎 Bind description: Binds the request and response items to a struct. sidebar_position: 4 +toc_max_heading_level: 4 --- -:::caution +Bindings are used to parse the request/response body, query parameters, cookies and much more into a struct. + +:::info -Documentation is still in progress. +All binder returned value are only valid within the handler. Do not store any references. +Make copies or use the [**`Immutable`**](./ctx.md) setting instead. [Read more...](../#zero-allocation) ::: -Bindings are used to parse the request/response body, query parameters, cookies and much more into a struct. + +## Binders - [Body](#body) + - [Form](#form) + - [JSON](#json) + - [MultipartForm](#multipartform) + - [XML](#xml) - [Cookie](#cookie) - [Header](#header) -- [ParamsParser](#paramsparser) - [Query](#query) - [RespHeader](#respheader) -- [SetParserDecoder](#setparserdecoder) +- [URI](#uri) - -> _Returned value is only valid within the handler. Do not store any references. -> Make copies or use the_ [_**`Immutable`**_](./ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) - - -## Body +### Body Binds the request body to a struct. @@ -52,16 +55,16 @@ type Person struct { } 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 - - // ... + 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 @@ -77,103 +80,226 @@ app.Post("/", func(c fiber.Ctx) error { // curl -X POST "http://localhost:3000/?name=john&pass=doe" ``` -## Cookie -This method is similar to [Body-Binding](#body), 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 of `cookie:"age"`. +**The methods for the various bodies can also be used directly:** + +#### Form + +Binds the request form 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 Form body with a field called Pass, you would use a struct field of `form:"pass"`. + ```go title="Signature" -func (b *Bind) Cookie(out any) error +func (b *Bind) Form(out any) error ``` ```go title="Example" // Field names should start with an uppercase letter type Person struct { - Name string `cookie:"name"` - Age int `cookie:"age"` - Job bool `cookie:"job"` + Name string `form:"name"` + Pass string `form:"pass"` } -app.Get("/", func(c fiber.Ctx) error { - p := new(Person) +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 + + // ... +}) - if err := c.Bind().Cookie(p); err != nil { - return err - } +// Run tests with the following curl commands - log.Println(p.Name) // Joseph - log.Println(p.Age) // 23 - log.Println(p.Job) // true -}) -// Run tests with the following curl command -// curl.exe --cookie "name=Joseph; age=23; job=true" http://localhost:8000/ +// curl -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "name=john&pass=doe" localhost:3000 ``` +#### JSON -## Header +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 of `json:"pass"`. -This method is similar to [Body-Binding](#body), but for request headers. -It is important to use the struct tag "reqHeader". For example, if you want to parse a request header with a field called Pass, you would use a struct field of `reqHeader:"pass"`. ```go title="Signature" -func (b *Bind) Header(out any) error +func (b *Bind) JSON(out any) error ``` ```go title="Example" // Field names should start with an uppercase letter type Person struct { - Name string `reqHeader:"name"` - Pass string `reqHeader:"pass"` - Products []string `reqHeader:"products"` + Name string `json:"name"` + Pass string `json:"pass"` } -app.Get("/", func(c fiber.Ctx) error { - p := new(Person) +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 commands + +// curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"john\",\"pass\":\"doe\"}" localhost:3000 + +``` + +#### MultipartForm + +Binds the request multipart form 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 MultipartForm body with a field called Pass, you would use a struct field of `form:"pass"`. - 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] +```go title="Signature" +func (b *Bind) MultipartForm(out any) error +``` + +```go title="Example" +// Field names should start with an uppercase letter +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().MultipartForm(p); err != nil { + return err + } + + log.Println(p.Name) // john + log.Println(p.Pass) // doe + + // ... }) -// Run tests with the following curl command -// curl "http://localhost:3000/" -H "name: john" -H "pass: doe" -H "products: shoe,hat" +// Run tests with the following curl commands + +// curl -X POST -H "Content-Type: multipart/form-data" -F "name=john" -F "pass=doe" localhost:3000 + ``` +#### XML -## ParamsParser +Binds the request xml form body to a struct. -:::caution +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 XML body with a field called Pass, you would use a struct field of `xml:"pass"`. -Not finished yet. -::: +```go title="Signature" +func (b *Bind) XML(out any) error +``` + +```go title="Example" +// Field names should start with an uppercase letter +type Person struct { + Name string `xml:"name"` + Pass string `xml:"pass"` +} -This method is similar to [Body-Binding](#body), but for path parameters. It is important to use the struct tag "params". For example, if you want to parse a path parameter with a field called Pass, you would use a struct field of params:"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 commands + +// curl -X POST -H "Content-Type: application/xml" --data "johndoe" localhost:3000 +``` + + +### Cookie + +This method is similar to [Body-Binding](#body), 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 of `cookie:"age"`. ```go title="Signature" -func (b *Bind) Params(out any) error +func (b *Bind) Cookie(out any) error ``` ```go title="Example" -// GET http://example.com/user/111 -app.Get("/user/:id", func(c fiber.Ctx) error { - param := struct {ID uint `params:"id"`}{} +// Field names should start with an uppercase letter +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.exe --cookie "name=Joseph; age=23; job=true" http://localhost:8000/ +``` + + +### Header - c.ParamsParser(¶m) // "{"id": 111}" +This method is similar to [Body-Binding](#body), 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 of `header:"pass"`. + +```go title="Signature" +func (b *Bind) Header(out any) error +``` - // ... +```go title="Example" +// Field names should start with an uppercase letter +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 +### Query This method is similar to [Body-Binding](#body), 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 of `query:"pass"`. @@ -191,21 +317,21 @@ type Person struct { } 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 - // fiber.Config{EnableSplittingOnParsers: false} - default - log.Println(p.Products) // ["shoe,hat"] - // fiber.Config{EnableSplittingOnParsers: true} - // log.Println(p.Products) // ["shoe", "hat"] - - - // ... + p := new(Person) + + if err := c.Bind().Query(p); err != nil { + return err + } + + log.Println(p.Name) // john + log.Println(p.Pass) // doe + // fiber.Config{EnableSplittingOnParsers: false} - default + log.Println(p.Products) // ["shoe,hat"] + // fiber.Config{EnableSplittingOnParsers: true} + // log.Println(p.Products) // ["shoe", "hat"] + + + // ... }) // Run tests with the following curl command @@ -217,7 +343,7 @@ For more parser settings please look here [Config](fiber.md#config) ::: -## RespHeader +### RespHeader This method is similar to [Body-Binding](#body), but for response headers. It is important to use the struct tag "respHeader". For example, if you want to parse a request header with a field called Pass, you would use a struct field of `respHeader:"pass"`. @@ -235,21 +361,116 @@ type Person struct { } app.Get("/", func(c fiber.Ctx) error { - p := new(Person) + 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 + +:::caution + +Not finished yet. - 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] +This method is similar to [Body-Binding](#body), 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 of uri:"pass" - // ... +```go title="Signature" +func (b *Bind) URI(out any) error +``` + +```go title="Example" +// GET http://example.com/user/111 +app.Get("/user/:id", func(c fiber.Ctx) error { + param := struct {ID uint `uri:"id"`}{} + + c.Bind().URI(¶m) // "{"id": 111}" + + // ... }) -// Run tests with the following curl command -// curl "http://localhost:3000/" -H "name: john" -H "pass: doe" -H "products: shoe,hat" +``` + +## Custom + +To use custom binders, you have to use this method. + +You can register them from [RegisterCustomBinder](./app.md#registercustombinder) method of Fiber instance. + +```go title="Signature" +func (b *Bind) Custom(name string, dest any) error +``` + +```go title="Example" +app := fiber.New() + +// My custom binder +customBinder := &customBinder{} +// Name of custom binder, which will be used as Bind().Custom("name") +func (*customBinder) Name() string { + return "custom" +} +// Is used in the Body Bind method to check if the binder should be used for custom mime types +func (*customBinder) MIMETypes() []string { + return []string{"application/yaml"} +} +// Parse the body and bind it to the out interface +func (*customBinder) Parse(c Ctx, out any) error { + // parse yaml body + return yaml.Unmarshal(c.Body(), out) +} +// Register custom binder +app.RegisterCustomBinder(customBinder) + +// curl -X POST http://localhost:3000/custom -H "Content-Type: application/yaml" -d "name: John" +app.Post("/custom", func(c Ctx) error { + var user User + // output: {Name:John} + // Custom binder is used by the name + if err := c.Bind().Custom("custom", &user); err != nil { + return err + } + // ... + return c.JSON(user) +}) +``` + +Internally they are also used in the [Body](#body) method. +For this the MIMETypes method is used to check if the custom binder should be used for the given content type. + +## Options + +For more control over the error handling, you can use the following methods. + +### Must + +If you want to handle binder errors automatically, you can use Must. +If there's an error it'll return error and 400 as HTTP status. + +```go title="Signature" +func (b *Bind) Must() *Bind +``` + +### Should + +To handle binder errors manually, you can prefer Should method. +It's default behavior of binder. + +```go title="Signature" +func (b *Bind) Should() *Bind ``` @@ -259,13 +480,13 @@ Allow you to config BodyParser/QueryParser decoder, base on schema's options, pr ```go title="Signature" func SetParserDecoder(parserConfig fiber.ParserConfig{ - IgnoreUnknownKeys bool, - ParserType []fiber.ParserType{ - Customtype any, - Converter func(string) reflect.Value, - }, - ZeroEmpty bool, - SetAliasTag string, + IgnoreUnknownKeys bool, + ParserType []fiber.ParserType{ + Customtype any, + Converter func(string) reflect.Value, + }, + ZeroEmpty bool, + SetAliasTag string, }) ``` @@ -276,28 +497,28 @@ type CustomTime time.Time // String() returns the time in string func (ct *CustomTime) String() string { t := time.Time(*ct).String() - return t -} - -// Register the converter for CustomTime type format as 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(v) - } - return reflect.Value{} + return t + } + + // Register the converter for CustomTime type format as 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(v) + } + return reflect.Value{} } customTime := fiber.ParserType{ - Customtype: CustomTime{}, - Converter: timeConverter, + Customtype: CustomTime{}, + Converter: timeConverter, } // Add setting to the Decoder fiber.SetParserDecoder(fiber.ParserConfig{ - IgnoreUnknownKeys: true, - ParserType: []fiber.ParserType{customTime}, - ZeroEmpty: true, + IgnoreUnknownKeys: true, + ParserType: []fiber.ParserType{customTime}, + ZeroEmpty: true, }) // Example to use CustomType, you pause custom time format not in RFC3339 @@ -326,3 +547,45 @@ app.Get("/query", func(c fiber.Ctx) error { // 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](./fiber.md#config) + +Setup your validator in the config + +```go title="Example" +import "github.com/go-playground/validator/v10" + +type structValidator struct { + validate *validator.Validate +} + +// Validator needs to implement the Validate method +func (v *structValidator) Validate(out any) error { + return v.validate.Struct(out) +} + +// Setup your validator in the config +app := fiber.New(fiber.Config{ + StructValidator: &structValidator{validate: validator.New()}, +}) +``` + +```go title="Example" +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 {// <- here you receive the validation errors + return err + } +}) +``` diff --git a/versioned_sidebars/version-v3.x-sidebars.json b/versioned_sidebars/version-v3.x-sidebars.json index b596b8445f8..f74b5756e41 100644 --- a/versioned_sidebars/version-v3.x-sidebars.json +++ b/versioned_sidebars/version-v3.x-sidebars.json @@ -6,8 +6,8 @@ "id": "welcome" }, { - type: 'doc', - id: 'whats_new' + "type": "doc", + "id": "whats_new" }, { "type": "category",