bind.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. package fiber
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "reflect"
  7. "slices"
  8. "sync"
  9. "github.com/gofiber/fiber/v3/binder"
  10. "github.com/gofiber/schema"
  11. "github.com/gofiber/utils/v2"
  12. utilsbytes "github.com/gofiber/utils/v2/bytes"
  13. )
  14. // CustomBinder An interface to register custom binders.
  15. type CustomBinder interface {
  16. Name() string
  17. MIMETypes() []string
  18. Parse(c Ctx, out any) error
  19. }
  20. // StructValidator is an interface to register custom struct validator for binding.
  21. type StructValidator interface {
  22. Validate(out any) error
  23. }
  24. var bindPool = sync.Pool{
  25. New: func() any {
  26. return &Bind{
  27. dontHandleErrs: true,
  28. }
  29. },
  30. }
  31. // Bind provides helper methods for binding request data to Go values.
  32. // By default (manual mode), parsing failures are returned as *BindError; use errors.As to extract source and field details.
  33. // With WithAutoHandling(), parsing failures set HTTP 400 and return *Error instead.
  34. type Bind struct {
  35. ctx Ctx
  36. dontHandleErrs bool
  37. skipValidation bool
  38. }
  39. // BindError source constants for BindError.Source.
  40. const (
  41. BindSourceURI = "uri"
  42. BindSourceQuery = "query"
  43. BindSourceHeader = "header"
  44. BindSourceCookie = "cookie"
  45. BindSourceBody = "body"
  46. BindSourceRespHeader = "respHeader"
  47. )
  48. // BindError wraps a binding failure with the source and field that failed.
  49. // Use errors.As(err, &be) to extract it when you need to branch on source
  50. // (e.g. 404 for URI vs 400 for body).
  51. type BindError struct {
  52. Err error // underlying error; use errors.As to inspect
  53. Source string // binding source: uri, query, body, header, cookie, or respHeader (see BindSource* constants)
  54. Field string // struct field or tag key that failed (best-effort, may be empty)
  55. }
  56. func (e *BindError) Error() string {
  57. if e.Field != "" {
  58. return fmt.Sprintf("bind %q from %s: %v", e.Field, e.Source, e.Err)
  59. }
  60. return fmt.Sprintf("bind from %s: %v", e.Source, e.Err)
  61. }
  62. func (e *BindError) Unwrap() error {
  63. return e.Err
  64. }
  65. func extractFieldFromError(err error) string {
  66. var convErr schema.ConversionError
  67. if errors.As(err, &convErr) {
  68. return convErr.Key
  69. }
  70. var unknownKey schema.UnknownKeyError
  71. if errors.As(err, &unknownKey) {
  72. return unknownKey.Key
  73. }
  74. var emptyField schema.EmptyFieldError
  75. if errors.As(err, &emptyField) {
  76. return emptyField.Key
  77. }
  78. var multiErr schema.MultiError
  79. if errors.As(err, &multiErr) {
  80. for k := range multiErr {
  81. return k
  82. }
  83. }
  84. var unmarshalErr *json.UnmarshalTypeError
  85. if errors.As(err, &unmarshalErr) {
  86. return unmarshalErr.Field
  87. }
  88. return ""
  89. }
  90. func newBindError(source string, raw error) *BindError {
  91. return &BindError{Source: source, Field: extractFieldFromError(raw), Err: raw}
  92. }
  93. // AcquireBind returns Bind reference from bind pool.
  94. func AcquireBind() *Bind {
  95. b, ok := bindPool.Get().(*Bind)
  96. if !ok {
  97. panic(errBindPoolTypeAssertion)
  98. }
  99. return b
  100. }
  101. // ReleaseBind returns b acquired via Bind to bind pool.
  102. func ReleaseBind(b *Bind) {
  103. b.release()
  104. bindPool.Put(b)
  105. }
  106. // releasePooledBinder resets a binder and returns it to its pool.
  107. // It should be used with defer to ensure proper cleanup of pooled binders.
  108. func releasePooledBinder[T interface{ Reset() }](pool *sync.Pool, bind T) {
  109. bind.Reset()
  110. binder.PutToThePool(pool, bind)
  111. }
  112. func (b *Bind) release() {
  113. b.ctx = nil
  114. b.dontHandleErrs = true
  115. b.skipValidation = false
  116. }
  117. // WithoutAutoHandling If you want to handle binder errors manually, you can use `WithoutAutoHandling`.
  118. // It's default behavior of binder.
  119. func (b *Bind) WithoutAutoHandling() *Bind {
  120. b.dontHandleErrs = true
  121. return b
  122. }
  123. // WithAutoHandling If you want to handle binder errors automatically, you can use `WithAutoHandling`.
  124. // If there's an error, it will return the error and set HTTP status to `400 Bad Request`.
  125. // You must still return on error explicitly
  126. func (b *Bind) WithAutoHandling() *Bind {
  127. b.dontHandleErrs = false
  128. return b
  129. }
  130. // SkipValidation enables or disables struct validation for the current bind chain.
  131. func (b *Bind) SkipValidation(skip bool) *Bind {
  132. b.skipValidation = skip
  133. return b
  134. }
  135. // Check WithAutoHandling/WithoutAutoHandling errors and return it by usage.
  136. func (b *Bind) returnErr(err error) error {
  137. if err == nil || b.dontHandleErrs {
  138. return err
  139. }
  140. b.ctx.Status(StatusBadRequest)
  141. return NewError(StatusBadRequest, "Bad request: "+err.Error())
  142. }
  143. // returnBindErr runs returnErr and, if the result is not a *Error, wraps it in *BindError.
  144. // Use for binding parse failures; use returnErr directly for Custom and validation errors.
  145. func (b *Bind) returnBindErr(err error, source string) error {
  146. if retErr := b.returnErr(err); retErr != nil {
  147. var fiberErr *Error
  148. if errors.As(retErr, &fiberErr) {
  149. return fiberErr
  150. }
  151. return newBindError(source, retErr)
  152. }
  153. return nil
  154. }
  155. // Struct validation.
  156. func (b *Bind) validateStruct(out any) error {
  157. if b.skipValidation {
  158. return nil
  159. }
  160. validator := b.ctx.App().config.StructValidator
  161. if validator == nil {
  162. return nil
  163. }
  164. t := reflect.TypeOf(out)
  165. if t == nil {
  166. return nil
  167. }
  168. // Unwrap pointers (e.g. *T, **T) to inspect the underlying destination type.
  169. for t.Kind() == reflect.Ptr {
  170. t = t.Elem()
  171. }
  172. if t.Kind() != reflect.Struct {
  173. return nil
  174. }
  175. return validator.Validate(out)
  176. }
  177. // Custom To use custom binders, you have to use this method.
  178. // You can register them from RegisterCustomBinder method of Fiber instance.
  179. // They're checked by name, if it's not found, it will return an error.
  180. // NOTE: WithAutoHandling/WithAutoHandling is still valid for Custom binders.
  181. func (b *Bind) Custom(name string, dest any) error {
  182. binders := b.ctx.App().customBinders
  183. for _, customBinder := range binders {
  184. if customBinder.Name() == name {
  185. if err := b.returnBindErr(customBinder.Parse(b.ctx, dest), name); err != nil {
  186. return err
  187. }
  188. return b.validateStruct(dest)
  189. }
  190. }
  191. return ErrCustomBinderNotFound
  192. }
  193. // Header binds the request header strings into the struct, map[string]string and map[string][]string.
  194. // Returns *BindError on parse failure (manual mode) or *Error with status 400 (auto-handling mode).
  195. func (b *Bind) Header(out any) error {
  196. bind := binder.GetFromThePool[*binder.HeaderBinding](&binder.HeaderBinderPool)
  197. bind.EnableSplitting = b.ctx.App().config.EnableSplittingOnParsers
  198. defer releasePooledBinder(&binder.HeaderBinderPool, bind)
  199. if err := b.returnBindErr(bind.Bind(b.ctx.Request(), out), BindSourceHeader); err != nil {
  200. return err
  201. }
  202. return b.validateStruct(out)
  203. }
  204. // RespHeader binds the response header strings into the struct, map[string]string and map[string][]string.
  205. // Returns *BindError on parse failure (manual mode) or *Error with status 400 (auto-handling mode).
  206. func (b *Bind) RespHeader(out any) error {
  207. bind := binder.GetFromThePool[*binder.RespHeaderBinding](&binder.RespHeaderBinderPool)
  208. bind.EnableSplitting = b.ctx.App().config.EnableSplittingOnParsers
  209. defer releasePooledBinder(&binder.RespHeaderBinderPool, bind)
  210. if err := b.returnBindErr(bind.Bind(b.ctx.Response(), out), BindSourceRespHeader); err != nil {
  211. return err
  212. }
  213. return b.validateStruct(out)
  214. }
  215. // Cookie binds the request cookie strings into the struct, map[string]string and map[string][]string.
  216. // Returns *BindError on parse failure (manual mode) or *Error with status 400 (auto-handling mode).
  217. // NOTE: If your cookie is like key=val1,val2; they'll be bound as a slice if your map is map[string][]string. Else, it'll use last element of cookie.
  218. func (b *Bind) Cookie(out any) error {
  219. bind := binder.GetFromThePool[*binder.CookieBinding](&binder.CookieBinderPool)
  220. bind.EnableSplitting = b.ctx.App().config.EnableSplittingOnParsers
  221. defer releasePooledBinder(&binder.CookieBinderPool, bind)
  222. if err := b.returnBindErr(bind.Bind(&b.ctx.RequestCtx().Request, out), BindSourceCookie); err != nil {
  223. return err
  224. }
  225. return b.validateStruct(out)
  226. }
  227. // Query binds the query string into the struct, map[string]string and map[string][]string.
  228. // Returns *BindError on parse failure (manual mode) or *Error with status 400 (auto-handling mode).
  229. func (b *Bind) Query(out any) error {
  230. bind := binder.GetFromThePool[*binder.QueryBinding](&binder.QueryBinderPool)
  231. bind.EnableSplitting = b.ctx.App().config.EnableSplittingOnParsers
  232. defer releasePooledBinder(&binder.QueryBinderPool, bind)
  233. if err := b.returnBindErr(bind.Bind(&b.ctx.RequestCtx().Request, out), BindSourceQuery); err != nil {
  234. return err
  235. }
  236. return b.validateStruct(out)
  237. }
  238. // JSON binds the body string into the struct.
  239. // Returns *BindError on parse failure (manual mode) or *Error with status 400 (auto-handling mode).
  240. func (b *Bind) JSON(out any) error {
  241. bind := binder.GetFromThePool[*binder.JSONBinding](&binder.JSONBinderPool)
  242. bind.JSONDecoder = b.ctx.App().Config().JSONDecoder
  243. defer releasePooledBinder(&binder.JSONBinderPool, bind)
  244. if err := b.returnBindErr(bind.Bind(b.ctx.Body(), out), BindSourceBody); err != nil {
  245. return err
  246. }
  247. return b.validateStruct(out)
  248. }
  249. // CBOR binds the body string into the struct.
  250. // Returns *BindError on parse failure (manual mode) or *Error with status 400 (auto-handling mode).
  251. func (b *Bind) CBOR(out any) error {
  252. bind := binder.GetFromThePool[*binder.CBORBinding](&binder.CBORBinderPool)
  253. bind.CBORDecoder = b.ctx.App().Config().CBORDecoder
  254. defer releasePooledBinder(&binder.CBORBinderPool, bind)
  255. if err := b.returnBindErr(bind.Bind(b.ctx.Body(), out), BindSourceBody); err != nil {
  256. return err
  257. }
  258. return b.validateStruct(out)
  259. }
  260. // XML binds the body string into the struct.
  261. // Returns *BindError on parse failure (manual mode) or *Error with status 400 (auto-handling mode).
  262. func (b *Bind) XML(out any) error {
  263. bind := binder.GetFromThePool[*binder.XMLBinding](&binder.XMLBinderPool)
  264. bind.XMLDecoder = b.ctx.App().config.XMLDecoder
  265. defer releasePooledBinder(&binder.XMLBinderPool, bind)
  266. if err := b.returnBindErr(bind.Bind(b.ctx.Body(), out), BindSourceBody); err != nil {
  267. return err
  268. }
  269. return b.validateStruct(out)
  270. }
  271. // Form binds the form into the struct, map[string]string and map[string][]string.
  272. // Returns *BindError on parse failure (manual mode) or *Error with status 400 (auto-handling mode).
  273. // If Content-Type is "application/x-www-form-urlencoded" or "multipart/form-data", it will bind the form values.
  274. // Multipart file fields are supported using *multipart.FileHeader, []*multipart.FileHeader, or *[]*multipart.FileHeader.
  275. func (b *Bind) Form(out any) error {
  276. bind := binder.GetFromThePool[*binder.FormBinding](&binder.FormBinderPool)
  277. bind.EnableSplitting = b.ctx.App().config.EnableSplittingOnParsers
  278. bind.MaxBodySize = b.ctx.App().config.BodyLimit
  279. defer releasePooledBinder(&binder.FormBinderPool, bind)
  280. if err := b.returnBindErr(bind.Bind(&b.ctx.RequestCtx().Request, out), BindSourceBody); err != nil {
  281. return err
  282. }
  283. return b.validateStruct(out)
  284. }
  285. // URI binds the route parameters into the struct, map[string]string and map[string][]string.
  286. // Returns *BindError on parse failure (manual mode) or *Error with status 400 (auto-handling mode).
  287. func (b *Bind) URI(out any) error {
  288. bind := binder.GetFromThePool[*binder.URIBinding](&binder.URIBinderPool)
  289. defer releasePooledBinder(&binder.URIBinderPool, bind)
  290. if err := b.returnBindErr(bind.Bind(b.ctx.Route().Params, b.ctx.Params, out), BindSourceURI); err != nil {
  291. return err
  292. }
  293. return b.validateStruct(out)
  294. }
  295. // MsgPack binds the body string into the struct.
  296. // Returns *BindError on parse failure (manual mode) or *Error with status 400 (auto-handling mode).
  297. func (b *Bind) MsgPack(out any) error {
  298. bind := binder.GetFromThePool[*binder.MsgPackBinding](&binder.MsgPackBinderPool)
  299. bind.MsgPackDecoder = b.ctx.App().Config().MsgPackDecoder
  300. defer releasePooledBinder(&binder.MsgPackBinderPool, bind)
  301. if err := b.returnBindErr(bind.Bind(b.ctx.Body(), out), BindSourceBody); err != nil {
  302. return err
  303. }
  304. return b.validateStruct(out)
  305. }
  306. // Body binds the request body into the struct, map[string]string and map[string][]string.
  307. // Returns *BindError on parse failure (manual mode) or *Error with status 400 (auto-handling mode).
  308. // It supports decoding the following content types based on the Content-Type header:
  309. // application/json, application/xml, application/x-www-form-urlencoded, multipart/form-data
  310. // If none of the content types above are matched, it'll take a look custom binders by checking the MIMETypes() method of custom binder.
  311. // If there is no custom binder for mime type of body, it will return a ErrUnprocessableEntity error.
  312. func (b *Bind) Body(out any) error {
  313. // Get content-type
  314. ctype := utils.UnsafeString(utilsbytes.UnsafeToLower(b.ctx.RequestCtx().Request.Header.ContentType()))
  315. ctype = binder.FilterFlags(utils.ParseVendorSpecificContentType(ctype))
  316. // Check custom binders
  317. binders := b.ctx.App().customBinders
  318. for _, customBinder := range binders {
  319. if slices.Contains(customBinder.MIMETypes(), ctype) {
  320. if err := b.returnBindErr(customBinder.Parse(b.ctx, out), BindSourceBody); err != nil {
  321. return err
  322. }
  323. return b.validateStruct(out)
  324. }
  325. }
  326. // Parse body accordingly
  327. switch ctype {
  328. case MIMEApplicationJSON:
  329. return b.JSON(out)
  330. case MIMEApplicationMsgPack:
  331. return b.MsgPack(out)
  332. case MIMETextXML, MIMEApplicationXML:
  333. return b.XML(out)
  334. case MIMEApplicationCBOR:
  335. return b.CBOR(out)
  336. case MIMEApplicationForm, MIMEMultipartForm:
  337. return b.Form(out)
  338. }
  339. // No suitable content type found
  340. return ErrUnprocessableEntity
  341. }
  342. // All binds values from URI params, the request body, the query string,
  343. // headers, and cookies into the provided struct in precedence order.
  344. // Returns *BindError on parse failure (manual mode) or *Error with status 400 (auto-handling mode).
  345. func (b *Bind) All(out any) error {
  346. outVal := reflect.ValueOf(out)
  347. if outVal.Kind() != reflect.Ptr || outVal.Elem().Kind() != reflect.Struct {
  348. return ErrUnprocessableEntity
  349. }
  350. outElem := outVal.Elem()
  351. // Precedence: URL Params -> Body -> Query -> Headers -> Cookies
  352. sources := []func(any) error{b.URI}
  353. // Check if both Body and Content-Type are set
  354. if len(b.ctx.Request().Body()) > 0 && len(b.ctx.RequestCtx().Request.Header.ContentType()) > 0 {
  355. sources = append(sources, b.Body)
  356. }
  357. sources = append(sources, b.Query, b.Header, b.Cookie)
  358. prevSkip := b.skipValidation
  359. b.skipValidation = true
  360. // TODO: Support custom precedence with an optional binding_source tag
  361. // TODO: Create WithOverrideEmptyValues
  362. // Bind from each source, but only update unset fields
  363. for _, bindFunc := range sources {
  364. tempStruct := reflect.New(outElem.Type()).Interface()
  365. if err := bindFunc(tempStruct); err != nil {
  366. b.skipValidation = prevSkip
  367. return err
  368. }
  369. tempStructVal := reflect.ValueOf(tempStruct).Elem()
  370. mergeStruct(outElem, tempStructVal)
  371. }
  372. b.skipValidation = prevSkip
  373. return b.returnErr(b.validateStruct(out))
  374. }
  375. func mergeStruct(dst, src reflect.Value) {
  376. dstFields := dst.NumField()
  377. for i := range dstFields {
  378. dstField := dst.Field(i)
  379. srcField := src.Field(i)
  380. // Skip if the destination field is already set
  381. if isZero(dstField.Interface()) {
  382. if dstField.CanSet() && srcField.IsValid() {
  383. dstField.Set(srcField)
  384. }
  385. }
  386. }
  387. }
  388. func isZero(value any) bool {
  389. v := reflect.ValueOf(value)
  390. return v.IsZero()
  391. }