bind.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. package fiber
  2. import (
  3. "reflect"
  4. "slices"
  5. "sync"
  6. "github.com/gofiber/fiber/v3/binder"
  7. "github.com/gofiber/utils/v2"
  8. utilsbytes "github.com/gofiber/utils/v2/bytes"
  9. )
  10. // CustomBinder An interface to register custom binders.
  11. type CustomBinder interface {
  12. Name() string
  13. MIMETypes() []string
  14. Parse(c Ctx, out any) error
  15. }
  16. // StructValidator is an interface to register custom struct validator for binding.
  17. type StructValidator interface {
  18. Validate(out any) error
  19. }
  20. var bindPool = sync.Pool{
  21. New: func() any {
  22. return &Bind{
  23. dontHandleErrs: true,
  24. }
  25. },
  26. }
  27. // Bind provides helper methods for binding request data to Go values.
  28. type Bind struct {
  29. ctx Ctx
  30. dontHandleErrs bool
  31. skipValidation bool
  32. }
  33. // AcquireBind returns Bind reference from bind pool.
  34. func AcquireBind() *Bind {
  35. b, ok := bindPool.Get().(*Bind)
  36. if !ok {
  37. panic(errBindPoolTypeAssertion)
  38. }
  39. return b
  40. }
  41. // ReleaseBind returns b acquired via Bind to bind pool.
  42. func ReleaseBind(b *Bind) {
  43. b.release()
  44. bindPool.Put(b)
  45. }
  46. // releasePooledBinder resets a binder and returns it to its pool.
  47. // It should be used with defer to ensure proper cleanup of pooled binders.
  48. func releasePooledBinder[T interface{ Reset() }](pool *sync.Pool, bind T) {
  49. bind.Reset()
  50. binder.PutToThePool(pool, bind)
  51. }
  52. func (b *Bind) release() {
  53. b.ctx = nil
  54. b.dontHandleErrs = true
  55. b.skipValidation = false
  56. }
  57. // WithoutAutoHandling If you want to handle binder errors manually, you can use `WithoutAutoHandling`.
  58. // It's default behavior of binder.
  59. func (b *Bind) WithoutAutoHandling() *Bind {
  60. b.dontHandleErrs = true
  61. return b
  62. }
  63. // WithAutoHandling If you want to handle binder errors automatically, you can use `WithAutoHandling`.
  64. // If there's an error, it will return the error and set HTTP status to `400 Bad Request`.
  65. // You must still return on error explicitly
  66. func (b *Bind) WithAutoHandling() *Bind {
  67. b.dontHandleErrs = false
  68. return b
  69. }
  70. // SkipValidation enables or disables struct validation for the current bind chain.
  71. func (b *Bind) SkipValidation(skip bool) *Bind {
  72. b.skipValidation = skip
  73. return b
  74. }
  75. // Check WithAutoHandling/WithoutAutoHandling errors and return it by usage.
  76. func (b *Bind) returnErr(err error) error {
  77. if err == nil || b.dontHandleErrs {
  78. return err
  79. }
  80. b.ctx.Status(StatusBadRequest)
  81. return NewError(StatusBadRequest, "Bad request: "+err.Error())
  82. }
  83. // Struct validation.
  84. func (b *Bind) validateStruct(out any) error {
  85. if b.skipValidation {
  86. return nil
  87. }
  88. validator := b.ctx.App().config.StructValidator
  89. if validator == nil {
  90. return nil
  91. }
  92. t := reflect.TypeOf(out)
  93. if t == nil {
  94. return nil
  95. }
  96. // Unwrap pointers (e.g. *T, **T) to inspect the underlying destination type.
  97. for t.Kind() == reflect.Ptr {
  98. t = t.Elem()
  99. }
  100. if t.Kind() != reflect.Struct {
  101. return nil
  102. }
  103. return validator.Validate(out)
  104. }
  105. // Custom To use custom binders, you have to use this method.
  106. // You can register them from RegisterCustomBinder method of Fiber instance.
  107. // They're checked by name, if it's not found, it will return an error.
  108. // NOTE: WithAutoHandling/WithAutoHandling is still valid for Custom binders.
  109. func (b *Bind) Custom(name string, dest any) error {
  110. binders := b.ctx.App().customBinders
  111. for _, customBinder := range binders {
  112. if customBinder.Name() == name {
  113. return b.returnErr(customBinder.Parse(b.ctx, dest))
  114. }
  115. }
  116. return ErrCustomBinderNotFound
  117. }
  118. // Header binds the request header strings into the struct, map[string]string and map[string][]string.
  119. func (b *Bind) Header(out any) error {
  120. bind := binder.GetFromThePool[*binder.HeaderBinding](&binder.HeaderBinderPool)
  121. bind.EnableSplitting = b.ctx.App().config.EnableSplittingOnParsers
  122. defer releasePooledBinder(&binder.HeaderBinderPool, bind)
  123. if err := b.returnErr(bind.Bind(b.ctx.Request(), out)); err != nil {
  124. return err
  125. }
  126. return b.validateStruct(out)
  127. }
  128. // RespHeader binds the response header strings into the struct, map[string]string and map[string][]string.
  129. func (b *Bind) RespHeader(out any) error {
  130. bind := binder.GetFromThePool[*binder.RespHeaderBinding](&binder.RespHeaderBinderPool)
  131. bind.EnableSplitting = b.ctx.App().config.EnableSplittingOnParsers
  132. defer releasePooledBinder(&binder.RespHeaderBinderPool, bind)
  133. if err := b.returnErr(bind.Bind(b.ctx.Response(), out)); err != nil {
  134. return err
  135. }
  136. return b.validateStruct(out)
  137. }
  138. // Cookie binds the request cookie strings into the struct, map[string]string and map[string][]string.
  139. // 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.
  140. func (b *Bind) Cookie(out any) error {
  141. bind := binder.GetFromThePool[*binder.CookieBinding](&binder.CookieBinderPool)
  142. bind.EnableSplitting = b.ctx.App().config.EnableSplittingOnParsers
  143. defer releasePooledBinder(&binder.CookieBinderPool, bind)
  144. if err := b.returnErr(bind.Bind(&b.ctx.RequestCtx().Request, out)); err != nil {
  145. return err
  146. }
  147. return b.validateStruct(out)
  148. }
  149. // Query binds the query string into the struct, map[string]string and map[string][]string.
  150. func (b *Bind) Query(out any) error {
  151. bind := binder.GetFromThePool[*binder.QueryBinding](&binder.QueryBinderPool)
  152. bind.EnableSplitting = b.ctx.App().config.EnableSplittingOnParsers
  153. defer releasePooledBinder(&binder.QueryBinderPool, bind)
  154. if err := b.returnErr(bind.Bind(&b.ctx.RequestCtx().Request, out)); err != nil {
  155. return err
  156. }
  157. return b.validateStruct(out)
  158. }
  159. // JSON binds the body string into the struct.
  160. func (b *Bind) JSON(out any) error {
  161. bind := binder.GetFromThePool[*binder.JSONBinding](&binder.JSONBinderPool)
  162. bind.JSONDecoder = b.ctx.App().Config().JSONDecoder
  163. defer releasePooledBinder(&binder.JSONBinderPool, bind)
  164. if err := b.returnErr(bind.Bind(b.ctx.Body(), out)); err != nil {
  165. return err
  166. }
  167. return b.validateStruct(out)
  168. }
  169. // CBOR binds the body string into the struct.
  170. func (b *Bind) CBOR(out any) error {
  171. bind := binder.GetFromThePool[*binder.CBORBinding](&binder.CBORBinderPool)
  172. bind.CBORDecoder = b.ctx.App().Config().CBORDecoder
  173. defer releasePooledBinder(&binder.CBORBinderPool, bind)
  174. if err := b.returnErr(bind.Bind(b.ctx.Body(), out)); err != nil {
  175. return err
  176. }
  177. return b.validateStruct(out)
  178. }
  179. // XML binds the body string into the struct.
  180. func (b *Bind) XML(out any) error {
  181. bind := binder.GetFromThePool[*binder.XMLBinding](&binder.XMLBinderPool)
  182. bind.XMLDecoder = b.ctx.App().config.XMLDecoder
  183. defer releasePooledBinder(&binder.XMLBinderPool, bind)
  184. if err := b.returnErr(bind.Bind(b.ctx.Body(), out)); err != nil {
  185. return err
  186. }
  187. return b.validateStruct(out)
  188. }
  189. // Form binds the form into the struct, map[string]string and map[string][]string.
  190. // If Content-Type is "application/x-www-form-urlencoded" or "multipart/form-data", it will bind the form values.
  191. // Multipart file fields are supported using *multipart.FileHeader, []*multipart.FileHeader, or *[]*multipart.FileHeader.
  192. func (b *Bind) Form(out any) error {
  193. bind := binder.GetFromThePool[*binder.FormBinding](&binder.FormBinderPool)
  194. bind.EnableSplitting = b.ctx.App().config.EnableSplittingOnParsers
  195. defer releasePooledBinder(&binder.FormBinderPool, bind)
  196. if err := b.returnErr(bind.Bind(&b.ctx.RequestCtx().Request, out)); err != nil {
  197. return err
  198. }
  199. return b.validateStruct(out)
  200. }
  201. // URI binds the route parameters into the struct, map[string]string and map[string][]string.
  202. func (b *Bind) URI(out any) error {
  203. bind := binder.GetFromThePool[*binder.URIBinding](&binder.URIBinderPool)
  204. defer releasePooledBinder(&binder.URIBinderPool, bind)
  205. if err := b.returnErr(bind.Bind(b.ctx.Route().Params, b.ctx.Params, out)); err != nil {
  206. return err
  207. }
  208. return b.validateStruct(out)
  209. }
  210. // MsgPack binds the body string into the struct.
  211. func (b *Bind) MsgPack(out any) error {
  212. bind := binder.GetFromThePool[*binder.MsgPackBinding](&binder.MsgPackBinderPool)
  213. bind.MsgPackDecoder = b.ctx.App().Config().MsgPackDecoder
  214. defer releasePooledBinder(&binder.MsgPackBinderPool, bind)
  215. if err := b.returnErr(bind.Bind(b.ctx.Body(), out)); err != nil {
  216. return err
  217. }
  218. return b.validateStruct(out)
  219. }
  220. // Body binds the request body into the struct, map[string]string and map[string][]string.
  221. // It supports decoding the following content types based on the Content-Type header:
  222. // application/json, application/xml, application/x-www-form-urlencoded, multipart/form-data
  223. // If none of the content types above are matched, it'll take a look custom binders by checking the MIMETypes() method of custom binder.
  224. // If there is no custom binder for mime type of body, it will return a ErrUnprocessableEntity error.
  225. func (b *Bind) Body(out any) error {
  226. // Get content-type
  227. ctype := utils.UnsafeString(utilsbytes.UnsafeToLower(b.ctx.RequestCtx().Request.Header.ContentType()))
  228. ctype = binder.FilterFlags(utils.ParseVendorSpecificContentType(ctype))
  229. // Check custom binders
  230. binders := b.ctx.App().customBinders
  231. for _, customBinder := range binders {
  232. if slices.Contains(customBinder.MIMETypes(), ctype) {
  233. return b.returnErr(customBinder.Parse(b.ctx, out))
  234. }
  235. }
  236. // Parse body accordingly
  237. switch ctype {
  238. case MIMEApplicationJSON:
  239. return b.JSON(out)
  240. case MIMEApplicationMsgPack:
  241. return b.MsgPack(out)
  242. case MIMETextXML, MIMEApplicationXML:
  243. return b.XML(out)
  244. case MIMEApplicationCBOR:
  245. return b.CBOR(out)
  246. case MIMEApplicationForm, MIMEMultipartForm:
  247. return b.Form(out)
  248. }
  249. // No suitable content type found
  250. return ErrUnprocessableEntity
  251. }
  252. // All binds values from URI params, the request body, the query string,
  253. // headers, and cookies into the provided struct in precedence order.
  254. func (b *Bind) All(out any) error {
  255. outVal := reflect.ValueOf(out)
  256. if outVal.Kind() != reflect.Ptr || outVal.Elem().Kind() != reflect.Struct {
  257. return ErrUnprocessableEntity
  258. }
  259. outElem := outVal.Elem()
  260. // Precedence: URL Params -> Body -> Query -> Headers -> Cookies
  261. sources := []func(any) error{b.URI}
  262. // Check if both Body and Content-Type are set
  263. if len(b.ctx.Request().Body()) > 0 && len(b.ctx.RequestCtx().Request.Header.ContentType()) > 0 {
  264. sources = append(sources, b.Body)
  265. }
  266. sources = append(sources, b.Query, b.Header, b.Cookie)
  267. prevSkip := b.skipValidation
  268. b.skipValidation = true
  269. // TODO: Support custom precedence with an optional binding_source tag
  270. // TODO: Create WithOverrideEmptyValues
  271. // Bind from each source, but only update unset fields
  272. for _, bindFunc := range sources {
  273. tempStruct := reflect.New(outElem.Type()).Interface()
  274. if err := bindFunc(tempStruct); err != nil {
  275. b.skipValidation = prevSkip
  276. return err
  277. }
  278. tempStructVal := reflect.ValueOf(tempStruct).Elem()
  279. mergeStruct(outElem, tempStructVal)
  280. }
  281. b.skipValidation = prevSkip
  282. return b.returnErr(b.validateStruct(out))
  283. }
  284. func mergeStruct(dst, src reflect.Value) {
  285. dstFields := dst.NumField()
  286. for i := range dstFields {
  287. dstField := dst.Field(i)
  288. srcField := src.Field(i)
  289. // Skip if the destination field is already set
  290. if isZero(dstField.Interface()) {
  291. if dstField.CanSet() && srcField.IsValid() {
  292. dstField.Set(srcField)
  293. }
  294. }
  295. }
  296. }
  297. func isZero(value any) bool {
  298. v := reflect.ValueOf(value)
  299. return v.IsZero()
  300. }