diff --git a/bind.go b/bind.go
index eff595cd72..ad76594265 100644
--- a/bind.go
+++ b/bind.go
@@ -19,34 +19,35 @@ type StructValidator interface {
// Bind struct
type Bind struct {
- ctx Ctx
- should bool
+ ctx Ctx
+ dontHandleErrs bool
}
-// Should To handle binder errors manually, you can prefer Should method.
+// If you want to handle binder errors manually, you can use `WithoutAutoHandling`.
// It's default behavior of binder.
-func (b *Bind) Should() *Bind {
- b.should = true
+func (b *Bind) WithoutAutoHandling() *Bind {
+ b.dontHandleErrs = true
return b
}
-// 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.
-func (b *Bind) Must() *Bind {
- b.should = false
+// 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`.
+// You must still return on error explicitly
+func (b *Bind) WithAutoHandling() *Bind {
+ b.dontHandleErrs = false
return b
}
-// Check Should/Must errors and return it by usage.
+// Check WithAutoHandling/WithoutAutoHandling errors and return it by usage.
func (b *Bind) returnErr(err error) error {
- if !b.should {
- b.ctx.Status(StatusBadRequest)
- return NewError(StatusBadRequest, "Bad request: "+err.Error())
+ if err == nil || b.dontHandleErrs {
+ return err
}
- return err
+ b.ctx.Status(StatusBadRequest)
+ return NewError(StatusBadRequest, "Bad request: "+err.Error())
}
// Struct validation.
@@ -62,7 +63,7 @@ func (b *Bind) validateStruct(out any) error {
// Custom To use custom binders, you have to use this method.
// You can register them from RegisterCustomBinder method of Fiber instance.
// They're checked by name, if it's not found, it will return an error.
-// NOTE: Should/Must is still valid for Custom binders.
+// NOTE: WithAutoHandling/WithAutoHandling is still valid for Custom binders.
func (b *Bind) Custom(name string, dest any) error {
binders := b.ctx.App().customBinders
for _, customBinder := range binders {
@@ -92,7 +93,7 @@ func (b *Bind) RespHeader(out any) error {
return b.validateStruct(out)
}
-// Cookie binds the requesr cookie strings into the struct, map[string]string and map[string][]string.
+// Cookie binds the request cookie strings into the struct, map[string]string and map[string][]string.
// NOTE: If your cookie is like key=val1,val2; they'll be binded as an slice if your map is map[string][]string. Else, it'll use last element of cookie.
func (b *Bind) Cookie(out any) error {
if err := b.returnErr(binder.CookieBinder.Bind(b.ctx.RequestCtx(), out)); err != nil {
diff --git a/bind_test.go b/bind_test.go
index aa00e191ca..a75722b3b4 100644
--- a/bind_test.go
+++ b/bind_test.go
@@ -19,6 +19,15 @@ import (
const helloWorld = "hello world"
+// go test -run Test_returnErr -v
+func Test_returnErr(t *testing.T) {
+ app := New()
+ c := app.AcquireCtx(&fasthttp.RequestCtx{})
+
+ err := c.Bind().WithAutoHandling().returnErr(nil)
+ require.NoError(t, err)
+}
+
// go test -run Test_Bind_Query -v
func Test_Bind_Query(t *testing.T) {
t.Parallel()
@@ -1616,8 +1625,8 @@ func Test_Bind_CustomBinder(t *testing.T) {
require.Equal(t, "john", d.Name)
}
-// go test -run Test_Bind_Must
-func Test_Bind_Must(t *testing.T) {
+// go test -run Test_Bind_WithAutoHandling
+func Test_Bind_WithAutoHandling(t *testing.T) {
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
@@ -1626,7 +1635,7 @@ func Test_Bind_Must(t *testing.T) {
}
rq := new(RequiredQuery)
c.Request().URI().SetQueryString("")
- err := c.Bind().Must().Query(rq)
+ err := c.Bind().WithAutoHandling().Query(rq)
require.Equal(t, StatusBadRequest, c.Response().StatusCode())
require.Equal(t, "Bad request: bind: name is empty", err.Error())
}
diff --git a/binder/README.md b/binder/README.md
index 676e1c9e89..6794a3b93b 100644
--- a/binder/README.md
+++ b/binder/README.md
@@ -1,17 +1,19 @@
# Fiber Binders
-Binder is a new request/response binding feature for Fiber. Against the old Fiber parsers, it supports custom binder registration, struct validation, `map[string]string`, `map[string][]string`, and more. It's introduced in Fiber v3 and a replacement of:
+**Binder** is a new request/response binding feature for Fiber introduced in Fiber v3. It replaces the old Fiber parsers and offers enhanced capabilities such as custom binder registration, struct validation, support for `map[string]string`, `map[string][]string`, and more. Binder replaces the following components:
-- BodyParser
-- ParamsParser
-- GetReqHeaders
-- GetRespHeaders
-- AllParams
-- QueryParser
-- ReqHeaderParser
+- `BodyParser`
+- `ParamsParser`
+- `GetReqHeaders`
+- `GetRespHeaders`
+- `AllParams`
+- `QueryParser`
+- `ReqHeaderParser`
## Default Binders
+Fiber provides several default binders out of the box:
+
- [Form](form.go)
- [Query](query.go)
- [URI](uri.go)
@@ -23,12 +25,12 @@ Binder is a new request/response binding feature for Fiber. Against the old Fibe
## Guides
-### Binding into the Struct
+### Binding into a Struct
-Fiber supports binding into the struct with [gorilla/schema](https://github.com/gorilla/schema). Here's an example:
+Fiber supports binding request data directly into a struct using [gorilla/schema](https://github.com/gorilla/schema). Here's an example:
```go
-// Field names should start with an uppercase letter
+// Field names must start with an uppercase letter
type Person struct {
Name string `json:"name" xml:"name" form:"name"`
Pass string `json:"pass" xml:"pass" form:"pass"`
@@ -41,56 +43,63 @@ app.Post("/", func(c fiber.Ctx) error {
return err
}
- log.Println(p.Name) // john
- log.Println(p.Pass) // doe
+ log.Println(p.Name) // Output: john
+ log.Println(p.Pass) // Output: doe
- // ...
+ // Additional logic...
})
// Run tests with the following curl commands:
-// curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"john\",\"pass\":\"doe\"}" localhost:3000
+// JSON
+curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"john\",\"pass\":\"doe\"}" localhost:3000
-// curl -X POST -H "Content-Type: application/xml" --data "johndoe" localhost:3000
+// XML
+curl -X POST -H "Content-Type: application/xml" --data "johndoe" localhost:3000
-// curl -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "name=john&pass=doe" localhost:3000
+// URL-Encoded Form
+curl -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "name=john&pass=doe" localhost:3000
-// curl -X POST -F name=john -F pass=doe http://localhost:3000
+// Multipart Form
+curl -X POST -F name=john -F pass=doe http://localhost:3000
-// curl -X POST "http://localhost:3000/?name=john&pass=doe"
+// Query Parameters
+curl -X POST "http://localhost:3000/?name=john&pass=doe"
```
-### Binding into the Map
+### Binding into a Map
-Fiber supports binding into the `map[string]string` or `map[string][]string`. Here's an example:
+Fiber allows binding request data into a `map[string]string` or `map[string][]string`. Here's an example:
```go
app.Get("/", func(c fiber.Ctx) error {
- p := make(map[string][]string)
+ params := make(map[string][]string)
- if err := c.Bind().Query(p); err != nil {
+ if err := c.Bind().Query(params); err != nil {
return err
}
- log.Println(p["name"]) // john
- log.Println(p["pass"]) // doe
- log.Println(p["products"]) // [shoe, hat]
+ log.Println(params["name"]) // Output: [john]
+ log.Println(params["pass"]) // Output: [doe]
+ log.Println(params["products"]) // Output: [shoe hat]
- // ...
+ // Additional logic...
+ return nil
})
+
// Run tests with the following curl command:
-// curl "http://localhost:3000/?name=john&pass=doe&products=shoe,hat"
+curl "http://localhost:3000/?name=john&pass=doe&products=shoe&products=hat"
```
-### Behaviors of Should/Must
+### Automatic Error Handling with `WithAutoHandling`
-Normally, Fiber returns binder error directly. However; if you want to handle it automatically, you can prefer `Must()`.
+By default, Fiber returns binder errors directly. To handle errors automatically and return a `400 Bad Request` status, use the `WithAutoHandling()` method.
-If there's an error it'll return error and 400 as HTTP status. Here's an example for it:
+**Example:**
```go
-// Field names should start with an uppercase letter
+// Field names must start with an uppercase letter
type Person struct {
Name string `json:"name,required"`
Pass string `json:"pass"`
@@ -99,23 +108,24 @@ type Person struct {
app.Get("/", func(c fiber.Ctx) error {
p := new(Person)
- if err := c.Bind().Must().JSON(p); err != nil {
+ if err := c.Bind().WithAutoHandling().JSON(p); err != nil {
return err
- // Status code: 400
+ // Automatically returns status code 400
// Response: Bad request: name is empty
}
- // ...
+ // Additional logic...
+ return nil
})
// Run tests with the following curl command:
-// curl -X GET -H "Content-Type: application/json" --data "{\"pass\":\"doe\"}" localhost:3000
+curl -X GET -H "Content-Type: application/json" --data "{\"pass\":\"doe\"}" localhost:3000
```
-### Defining Custom Binder
+### Defining a Custom Binder
-We didn't add much binder to make Fiber codebase minimal. If you want to use your own binders, it's easy to register and use them. Here's an example for TOML binder.
+Fiber maintains a minimal codebase by not including every possible binder. If you need to use a custom binder, you can easily register and utilize it. Here's an example of creating a `toml` binder.
```go
type Person struct {
@@ -147,24 +157,26 @@ func main() {
return err
}
- // or you can use like:
+ // Alternatively, specify the custom binder:
// if err := c.Bind().Custom("toml", out); err != nil {
- // return err
+ // return err
// }
- return c.SendString(out.Pass) // test
+ return c.SendString(out.Pass) // Output: test
})
app.Listen(":3000")
}
-// curl -X GET -H "Content-Type: application/toml" --data "name = 'bar'
-// pass = 'test'" localhost:3000
+// Run tests with the following curl command:
+
+curl -X GET -H "Content-Type: application/toml" --data "name = 'bar'
+pass = 'test'" localhost:3000
```
-### Defining Custom Validator
+### Defining a Custom Validator
-All Fiber binders supporting struct validation if you defined validator inside of the config. You can create own validator, or use [go-playground/validator](https://github.com/go-playground/validator), [go-ozzo/ozzo-validation](https://github.com/go-ozzo/ozzo-validation)... Here's an example of simple custom validator:
+All Fiber binders support struct validation if a validator is defined in the configuration. You can create your own validator or use existing ones like [go-playground/validator](https://github.com/go-playground/validator) or [go-ozzo/ozzo-validation](https://github.com/go-ozzo/ozzo-validation). Here's an example of a simple custom validator:
```go
type Query struct {
@@ -174,27 +186,29 @@ type Query struct {
type structValidator struct{}
func (v *structValidator) Engine() any {
- return ""
+ return nil // Implement if using an external validation engine
}
func (v *structValidator) ValidateStruct(out any) error {
- out = reflect.ValueOf(out).Elem().Interface()
- sq := out.(Query)
+ data := reflect.ValueOf(out).Elem().Interface()
+ query := data.(Query)
- if sq.Name != "john" {
- return errors.New("you should have entered right name!")
+ if query.Name != "john" {
+ return errors.New("you should have entered the correct name!")
}
return nil
}
func main() {
- app := fiber.New(fiber.Config{StructValidator: &structValidator{}})
+ app := fiber.New(fiber.Config{
+ StructValidator: &structValidator{},
+ })
app.Get("/", func(c fiber.Ctx) error {
out := new(Query)
if err := c.Bind().Query(out); err != nil {
- return err // you should have entered right name!
+ return err // Returns: you should have entered the correct name!
}
return c.SendString(out.Name)
})
@@ -204,5 +218,5 @@ func main() {
// Run tests with the following curl command:
-// curl "http://localhost:3000/?name=efe"
+curl "http://localhost:3000/?name=efe"
```
diff --git a/ctx.go b/ctx.go
index a2eee2754b..14fb7246b0 100644
--- a/ctx.go
+++ b/ctx.go
@@ -640,7 +640,7 @@ func (c *DefaultCtx) Get(key string, defaultValue ...string) string {
}
// GetReqHeader returns the HTTP request header specified by filed.
-// This function is generic and can handle differnet headers type values.
+// This function is generic and can handle different headers type values.
func GetReqHeader[V GenericType](c Ctx, key string, defaultValue ...V) V {
var v V
return genericParseType[V](c.App().getString(c.Request().Header.Peek(key)), v, defaultValue...)
@@ -1083,7 +1083,7 @@ func (c *DefaultCtx) Params(key string, defaultValue ...string) string {
}
// Params is used to get the route parameters.
-// This function is generic and can handle differnet route parameters type values.
+// This function is generic and can handle different route parameters type values.
//
// Example:
//
@@ -1860,8 +1860,8 @@ func (c *DefaultCtx) IsFromLocal() bool {
func (c *DefaultCtx) Bind() *Bind {
if c.bind == nil {
c.bind = &Bind{
- ctx: c,
- should: true,
+ ctx: c,
+ dontHandleErrs: true,
}
}
return c.bind
diff --git a/docs/api/bind.md b/docs/api/bind.md
index 2ad0854ca9..a1cbedd18d 100644
--- a/docs/api/bind.md
+++ b/docs/api/bind.md
@@ -458,22 +458,23 @@ The `MIMETypes` method is used to check if the custom binder should be used for
For more control over error handling, you can use the following methods.
-### Must
+### WithAutoHandling
-If you want to handle binder errors automatically, you can use `Must`.
+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
```go title="Signature"
-func (b *Bind) Must() *Bind
+func (b *Bind) WithAutoHandling() *Bind
```
-### Should
+### WithoutAutoHandling
-To handle binder errors manually, you can use the `Should` method.
+To handle binder errors manually, you can use the `WithoutAutoHandling` method.
It's the default behavior of the binder.
```go title="Signature"
-func (b *Bind) Should() *Bind
+func (b *Bind) WithoutAutoHandling() *Bind
```
## SetParserDecoder