diff --git a/bind_readme.md b/bind_readme.md index 77cc5773..c9364696 100644 --- a/bind_readme.md +++ b/bind_readme.md @@ -109,9 +109,10 @@ you need. ### Parse Request Body -you can call `ctx.BodyJSON(v any) error` or `BodyXML(v any) error` +you can call `Bind().JSON(v any)` / `Bind().XML(v any)` / `Bind().Form(v any)` / `Bind().Multipart(v any)` +to unmarshal request Body. -These methods will check content-type HTTP header and call configured JSON or XML decoder to unmarshal. +use `Bind().Strict()` to enable content-type checking. ```golang package main @@ -127,13 +128,21 @@ type Body struct { func main() { app := fiber.New() - app.Get("/:id", func(c fiber.Ctx) error { + 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) + }) } ``` diff --git a/binder.go b/binder.go index e651f323..1f2d651a 100644 --- a/binder.go +++ b/binder.go @@ -15,9 +15,10 @@ var binderPool = sync.Pool{New: func() any { }} type Bind struct { - err error - ctx Ctx - val any // last decoded val + err error + ctx Ctx + val any // last decoded val + strict bool } func (c *DefaultCtx) Bind() *Bind { @@ -26,27 +27,37 @@ func (c *DefaultCtx) Bind() *Bind { return b } -func (b *Bind) setErr(err error) *Bind { - b.err = err +func (b *Bind) Strict() *Bind { + b.strict = true return b } -func (b *Bind) HTTPErr() error { - if b.err != nil { - if fe, ok := b.err.(*Error); ok { - return fe - } - - return NewError(http.StatusBadRequest, b.err.Error()) - } - - return nil +func (b *Bind) setErr(err error) *Bind { + b.err = err + return b } func (b *Bind) reset() { b.ctx = nil b.val = nil b.err = nil + b.strict = false +} + +// HTTPErr return a wrapped fiber.Error for 400 http bad request. +// it's not safe to use after HTTPErr is called. +func (b *Bind) HTTPErr() error { + err := b.Err() + + if err != nil { + if fe, ok := err.(*Error); ok { + return fe + } + + return NewError(http.StatusBadRequest, err.Error()) + } + + return nil } // Err return binding error and put binder back to pool @@ -61,17 +72,18 @@ func (b *Bind) Err() error { } // JSON unmarshal body as json -// unlike `ctx.BodyJSON`, this will also check "content-type" HTTP header. func (b *Bind) JSON(v any) *Bind { if b.err != nil { return b } - if !bytes.HasPrefix(b.ctx.Request().Header.ContentType(), utils.UnsafeBytes(MIMEApplicationJSON)) { - return b.setErr(NewError(http.StatusUnsupportedMediaType, "expecting content-type \"application/json\"")) + if b.strict { + if !bytes.HasPrefix(b.ctx.Request().Header.ContentType(), utils.UnsafeBytes(MIMEApplicationJSON)) { + return b.setErr(NewError(http.StatusUnsupportedMediaType, "expecting content-type \"application/json\"")) + } } - if err := b.ctx.BodyJSON(v); err != nil { + if err := b.ctx.App().config.JSONDecoder(b.ctx.Body(), v); err != nil { return b.setErr(err) } @@ -80,17 +92,18 @@ func (b *Bind) JSON(v any) *Bind { } // XML unmarshal body as xml -// unlike `ctx.BodyXML`, this will also check "content-type" HTTP header. func (b *Bind) XML(v any) *Bind { if b.err != nil { return b } - if !bytes.HasPrefix(b.ctx.Request().Header.ContentType(), utils.UnsafeBytes(MIMEApplicationXML)) { - return b.setErr(NewError(http.StatusUnsupportedMediaType, "expecting content-type \"application/xml\"")) + if b.strict { + if !bytes.HasPrefix(b.ctx.Request().Header.ContentType(), utils.UnsafeBytes(MIMEApplicationXML)) { + return b.setErr(NewError(http.StatusUnsupportedMediaType, "expecting content-type \"application/xml\"")) + } } - if err := b.ctx.BodyXML(v); err != nil { + if err := b.ctx.App().config.XMLDecoder(b.ctx.Body(), v); err != nil { return b.setErr(err) } @@ -104,8 +117,10 @@ func (b *Bind) Form(v any) *Bind { return b } - if !bytes.HasPrefix(b.ctx.Request().Header.ContentType(), utils.UnsafeBytes(MIMEApplicationForm)) { - return b.setErr(NewError(http.StatusUnsupportedMediaType, "expecting content-type \"application/x-www-form-urlencoded\"")) + if b.strict { + if !bytes.HasPrefix(b.ctx.Request().Header.ContentType(), utils.UnsafeBytes(MIMEApplicationForm)) { + return b.setErr(NewError(http.StatusUnsupportedMediaType, "expecting content-type \"application/x-www-form-urlencoded\"")) + } } if err := b.formDecode(v); err != nil { @@ -123,8 +138,10 @@ func (b *Bind) Multipart(v any) *Bind { return b } - if !bytes.HasPrefix(b.ctx.Request().Header.ContentType(), utils.UnsafeBytes(MIMEMultipartForm)) { - return b.setErr(NewError(http.StatusUnsupportedMediaType, "expecting content-type \"multipart/form-data\"")) + if b.strict { + if !bytes.HasPrefix(b.ctx.Request().Header.ContentType(), utils.UnsafeBytes(MIMEMultipartForm)) { + return b.setErr(NewError(http.StatusUnsupportedMediaType, "expecting content-type \"multipart/form-data\"")) + } } if err := b.multipartDecode(v); err != nil { diff --git a/ctx.go b/ctx.go index 22939b6f..fc383c76 100644 --- a/ctx.go +++ b/ctx.go @@ -260,14 +260,6 @@ func (c *DefaultCtx) Body() []byte { return body } -func (c *DefaultCtx) BodyJSON(v any) error { - return c.app.config.JSONDecoder(c.Body(), v) -} - -func (c *DefaultCtx) BodyXML(v any) error { - return c.app.config.XMLDecoder(c.Body(), v) -} - // ClearCookie expires a specific cookie by key on the client side. // If no key is provided it expires all cookies that came with the request. func (c *DefaultCtx) ClearCookie(key ...string) { diff --git a/ctx_interface.go b/ctx_interface.go index 98e9cd31..92e17d63 100644 --- a/ctx_interface.go +++ b/ctx_interface.go @@ -58,12 +58,6 @@ type Ctx interface { // Make copies or use the Immutable setting instead. Body() []byte - // BodyJSON will unmarshal request body with Config.JSONDecoder - BodyJSON(v any) error - - // BodyXML will unmarshal request body with Config.XMLDecoder - BodyXML(v any) error - // ClearCookie expires a specific cookie by key on the client side. // If no key is provided it expires all cookies that came with the request. ClearCookie(key ...string)