Repository: gofiber/fiber
File: ctx.go (Go)

Code before:

func decoderBuilder(parserConfig ParserConfig) interface{} {
	decoder := schema.NewDecoder()
	decoder.IgnoreUnknownKeys(parserConfig.IgnoreUnknownKeys)
	if parserConfig.SetAliasTag != "" {
		decoder.SetAliasTag(parserConfig.SetAliasTag)
	}
	for _, v := range parserConfig.ParserType {
		decoder.RegisterConverter(reflect.ValueOf(v.Customtype).Interface(), v.Converter)
	}
	decoder.ZeroEmpty(parserConfig.ZeroEmpty)
	return decoder
}

// BodyParser binds the request body to a struct.
// It supports decoding the following content types based on the Content-Type header:
// application/json, application/problem+json, application/xml, application/x-www-form-urlencoded, multipart/form-data
// If none of the content types above are matched, it will return a ErrUnprocessableEntity error
func (c *Ctx) BodyParser(out interface{}) error {
	// Get content-type
	ctype := utils.ToLower(c.app.getString(c.fasthttp.Request.Header.ContentType()))

	ctype = utils.ParseVendorSpecificContentType(ctype)

	// Parse body accordingly
	if strings.HasPrefix(ctype, MIMEApplicationJSON) || strings.HasPrefix(ctype, MIMEApplicationProblemJSON) {
		return c.app.config.JSONDecoder(c.Body(), out)
	}
	if strings.HasPrefix(ctype, MIMEApplicationForm) {
		data := make(map[string][]string)
		var err error

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

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

			if strings.Contains(k, "[") {
				k, err = parseParamSquareBrackets(k)
			}

			if c.app.config.EnableSplittingOnParsers && strings.Contains(v, ",") && equalFieldType(out, reflect.Slice, k, bodyTag) {
				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)

Review comment (suggestion):
[RFC 6838](https://datatracker.ietf.org/doc/html/rfc6838#section-4.2.8) 
It might make sense to match anything that has the +json suffix here. e.g.
```go
var ctypeEnd int
if ctypeEnd = strings.IndexByte(ctype, ';'); ctypeEnd == -1 {
  ctypeEnd = len(ctype)
}
isJSON := strings.HasSuffix(utils.TrimRight(ctype, ' '), "+json")
```

Code after:

func decoderBuilder(parserConfig ParserConfig) interface{} {
	decoder := schema.NewDecoder()
	decoder.IgnoreUnknownKeys(parserConfig.IgnoreUnknownKeys)
	if parserConfig.SetAliasTag != "" {
		decoder.SetAliasTag(parserConfig.SetAliasTag)
	}
	for _, v := range parserConfig.ParserType {
		decoder.RegisterConverter(reflect.ValueOf(v.Customtype).Interface(), v.Converter)
	}
	decoder.ZeroEmpty(parserConfig.ZeroEmpty)
	return decoder
}

// BodyParser binds the request body to a struct.
// 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
// All JSON extenstion mime types are supported (eg. application/problem+json)
// If none of the content types above are matched, it will return a ErrUnprocessableEntity error
func (c *Ctx) BodyParser(out interface{}) error {
	// Get content-type
	ctype := utils.ToLower(c.app.getString(c.fasthttp.Request.Header.ContentType()))

	ctype = utils.ParseVendorSpecificContentType(ctype)

	// Only use ctype string up to and excluding byte ';'
	ctypeEnd := strings.IndexByte(ctype, ';')
	if ctypeEnd != -1 {
		ctype = ctype[:ctypeEnd]
	}

	// Parse body accordingly
	if strings.HasSuffix(ctype, "json") {
		return c.app.config.JSONDecoder(c.Body(), out)
	}
	if strings.HasPrefix(ctype, MIMEApplicationForm) {
		data := make(map[string][]string)
		var err error

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

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

			if strings.Contains(k, "[") {
				k, err = parseParamSquareBrackets(k)
			}