Skip to content

Commit

Permalink
Feature Request: Support Square Bracket Notation in Multipart Form Data
Browse files Browse the repository at this point in the history
  • Loading branch information
ReneWerner87 committed Dec 31, 2024
1 parent a5f720a commit 86dcd09
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 80 deletions.
91 changes: 11 additions & 80 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -1284,95 +1284,26 @@ func (c *Ctx) QueryParser(out interface{}) error {
return c.parseToStruct(queryTag, out, data)
}

func parseParamSquareBrackets(k string) (string, error) {
bb := bytebufferpool.Get()
defer bytebufferpool.Put(bb)

kbytes := []byte(k)
openBracketsCount := 0

for i, b := range kbytes {
if b == '[' {
openBracketsCount++
if i+1 < len(kbytes) && kbytes[i+1] != ']' {
if err := bb.WriteByte('.'); err != nil {
return "", fmt.Errorf("failed to write: %w", err)
}
}
continue
}

if b == ']' {
openBracketsCount--
if openBracketsCount < 0 {
return "", errors.New("unmatched brackets")
}
continue
}

if err := bb.WriteByte(b); err != nil {
return "", fmt.Errorf("failed to write: %w", err)
}
}

if openBracketsCount > 0 {
return "", errors.New("unmatched brackets")
}

return bb.String(), nil
}

func formatParserData(out interface{}, data map[string][]string, aliasTag, key string, value interface{}, enableSplitting, supportBracketNotation bool) error { //nolint:revive // it's okay
var err error
if supportBracketNotation && strings.Contains(key, "[") {
key, err = parseParamSquareBrackets(key)
if err != nil {
return err
}
}

switch v := value.(type) {
case string:
assignBindData(out, data, aliasTag, key, v, enableSplitting)
case []string:
for _, val := range v {
assignBindData(out, data, aliasTag, key, val, enableSplitting)
}
default:
return fmt.Errorf("unsupported value type: %T", value)
}

return err
}

func assignBindData(out interface{}, data map[string][]string, aliasTag, key, value string, enableSplitting bool) { //nolint:revive // it's okay
if enableSplitting && strings.Contains(value, ",") && equalFieldType(out, reflect.Slice, key, aliasTag) {
values := strings.Split(value, ",")
for i := 0; i < len(values); i++ {
data[key] = append(data[key], values[i])
}
} else {
data[key] = append(data[key], value)
}
}

// ReqHeaderParser binds the request header strings to a struct.
func (c *Ctx) ReqHeaderParser(out interface{}) error {
data := make(map[string][]string)
var err error

c.fasthttp.Request.Header.VisitAll(func(key, val []byte) {
if err != nil {
return
}

k := c.app.getString(key)
v := c.app.getString(val)

if c.app.config.EnableSplittingOnParsers && strings.Contains(v, ",") && equalFieldType(out, reflect.Slice, k, reqHeaderTag) {
values := strings.Split(v, ",")
for i := 0; i < len(values); i++ {
data[k] = append(data[k], values[i])
}
} else {
data[k] = append(data[k], v)
}
err = formatParserData(out, data, reqHeaderTag, k, v, c.app.config.EnableSplittingOnParsers, false)
})

if err != nil {
return err
}

return c.parseToStruct(reqHeaderTag, out, data)
}

Expand Down
73 changes: 73 additions & 0 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package fiber
import (
"bytes"
"crypto/tls"
"errors"
"fmt"
"hash/crc32"
"io"
Expand Down Expand Up @@ -1151,3 +1152,75 @@ func IndexRune(str string, needle int32) bool {
}
return false
}

func parseParamSquareBrackets(k string) (string, error) {
bb := bytebufferpool.Get()
defer bytebufferpool.Put(bb)

kbytes := []byte(k)
openBracketsCount := 0

for i, b := range kbytes {
if b == '[' {
openBracketsCount++
if i+1 < len(kbytes) && kbytes[i+1] != ']' {
if err := bb.WriteByte('.'); err != nil {
return "", fmt.Errorf("failed to write: %w", err)
}
}
continue
}

if b == ']' {
openBracketsCount--
if openBracketsCount < 0 {
return "", errors.New("unmatched brackets")
}
continue
}

if err := bb.WriteByte(b); err != nil {
return "", fmt.Errorf("failed to write: %w", err)
}
}

if openBracketsCount > 0 {
return "", errors.New("unmatched brackets")
}

return bb.String(), nil
}

func formatParserData(out interface{}, data map[string][]string, aliasTag, key string, value interface{}, enableSplitting, supportBracketNotation bool) error { //nolint:revive // it's okay
var err error
if supportBracketNotation && strings.Contains(key, "[") {
key, err = parseParamSquareBrackets(key)
if err != nil {
return err
}
}

switch v := value.(type) {
case string:
assignBindData(out, data, aliasTag, key, v, enableSplitting)
case []string:
for _, val := range v {
assignBindData(out, data, aliasTag, key, val, enableSplitting)
}
default:
return fmt.Errorf("unsupported value type: %T", value)
}

return err
}

func assignBindData(out interface{}, data map[string][]string, aliasTag, key, value string, enableSplitting bool) { //nolint:revive // it's okay
if enableSplitting && strings.Contains(value, ",") && equalFieldType(out, reflect.Slice, key, aliasTag) {
values := strings.Split(value, ",")
for i := 0; i < len(values); i++ {
data[key] = append(data[key], values[i])
}
} else {
data[key] = append(data[key], value)
}
}

0 comments on commit 86dcd09

Please sign in to comment.