1
0
mirror of https://github.com/gofiber/fiber.git synced 2025-02-06 22:31:54 +00:00
fiber/bind.go
nickajacks1 92dd8d6917
🎨 Style: Add inamedparam linter (#2848)
inamedparam enforces that parameters in interface definitions be named.
This is important for clarity so that users and implementers can easily
understand the purpose of each parameter.
2024-02-10 22:36:49 +01:00

195 lines
5.5 KiB
Go

package fiber
import (
"github.com/gofiber/fiber/v3/binder"
"github.com/gofiber/utils/v2"
)
// An interface to register custom binders.
type CustomBinder interface {
Name() string
MIMETypes() []string
Parse(c Ctx, out any) error
}
// An interface to register custom struct validator for binding.
type StructValidator interface {
Engine() any
ValidateStruct(out any) error
}
// Bind struct
type Bind struct {
ctx *DefaultCtx
should bool
}
// To handle binder errors manually, you can prefer Should method.
// It's default behavior of binder.
func (b *Bind) Should() *Bind {
b.should = true
return b
}
// 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
return b
}
// Check Should/Must 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())
}
return err
}
// Struct validation.
func (b *Bind) validateStruct(out any) error {
validator := b.ctx.app.config.StructValidator
if validator != nil {
return validator.ValidateStruct(out)
}
return nil
}
// 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.
func (b *Bind) Custom(name string, dest any) error {
binders := b.ctx.App().customBinders
for _, customBinder := range binders {
if customBinder.Name() == name {
return b.returnErr(customBinder.Parse(b.ctx, dest))
}
}
return ErrCustomBinderNotFound
}
// Header binds the request header strings into the struct, map[string]string and map[string][]string.
func (b *Bind) Header(out any) error {
if err := b.returnErr(binder.HeaderBinder.Bind(b.ctx.Request(), out)); err != nil {
return err
}
return b.validateStruct(out)
}
// RespHeader binds the response header strings into the struct, map[string]string and map[string][]string.
func (b *Bind) RespHeader(out any) error {
if err := b.returnErr(binder.RespHeaderBinder.Bind(b.ctx.Response(), out)); err != nil {
return err
}
return b.validateStruct(out)
}
// Cookie binds the requesr 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.Context(), out)); err != nil {
return err
}
return b.validateStruct(out)
}
// QueryParser binds the query string into the struct, map[string]string and map[string][]string.
func (b *Bind) Query(out any) error {
if err := b.returnErr(binder.QueryBinder.Bind(b.ctx.Context(), out)); err != nil {
return err
}
return b.validateStruct(out)
}
// JSON binds the body string into the struct.
func (b *Bind) JSON(out any) error {
if err := b.returnErr(binder.JSONBinder.Bind(b.ctx.Body(), b.ctx.App().Config().JSONDecoder, out)); err != nil {
return err
}
return b.validateStruct(out)
}
// XML binds the body string into the struct.
func (b *Bind) XML(out any) error {
if err := b.returnErr(binder.XMLBinder.Bind(b.ctx.Body(), out)); err != nil {
return err
}
return b.validateStruct(out)
}
// Form binds the form into the struct, map[string]string and map[string][]string.
func (b *Bind) Form(out any) error {
if err := b.returnErr(binder.FormBinder.Bind(b.ctx.Context(), out)); err != nil {
return err
}
return b.validateStruct(out)
}
// URI binds the route parameters into the struct, map[string]string and map[string][]string.
func (b *Bind) URI(out any) error {
if err := b.returnErr(binder.URIBinder.Bind(b.ctx.route.Params, b.ctx.Params, out)); err != nil {
return err
}
return b.validateStruct(out)
}
// MultipartForm binds the multipart form into the struct, map[string]string and map[string][]string.
func (b *Bind) MultipartForm(out any) error {
if err := b.returnErr(binder.FormBinder.BindMultipart(b.ctx.Context(), out)); err != nil {
return err
}
return b.validateStruct(out)
}
// Body binds the request body into the struct, map[string]string and map[string][]string.
// It supports decoding the following content types based on the Content-Type header:
// application/json, application/xml, application/x-www-form-urlencoded, multipart/form-data
// If none of the content types above are matched, it'll take a look custom binders by checking the MIMETypes() method of custom binder.
// If there're no custom binder for mşme type of body, it will return a ErrUnprocessableEntity error.
func (b *Bind) Body(out any) error {
// Get content-type
ctype := utils.ToLower(utils.UnsafeString(b.ctx.Context().Request.Header.ContentType()))
ctype = binder.FilterFlags(utils.ParseVendorSpecificContentType(ctype))
// Parse body accordingly
switch ctype {
case MIMEApplicationJSON:
return b.JSON(out)
case MIMETextXML, MIMEApplicationXML:
return b.XML(out)
case MIMEApplicationForm:
return b.Form(out)
case MIMEMultipartForm:
return b.MultipartForm(out)
}
// Check custom binders
binders := b.ctx.App().customBinders
for _, customBinder := range binders {
for _, mime := range customBinder.MIMETypes() {
if mime == ctype {
return b.returnErr(customBinder.Parse(b.ctx, out))
}
}
}
// No suitable content type found
return ErrUnprocessableEntity
}