Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 [Bug]: BodyParser does not parse multipart.FileHeader fields in multipart/form-data requests #3286

Closed
3 tasks done
SadikSunbul opened this issue Jan 20, 2025 · 10 comments

Comments

@SadikSunbul
Copy link

SadikSunbul commented Jan 20, 2025

Bug Description

When using the BodyParser method in Fiber, it fails to automatically parse file fields (multipart.FileHeader) in multipart/form-data requests. While other fields like name and pass are successfully parsed, file fields remain nil, even if they are present in the request payload. This makes it necessary to manually retrieve files using the c.FormFile method, which defeats the purpose of automatic parsing via BodyParser.

How to Reproduce

1-> Create a POST request using curl or Postman with the following payload:
Field name: john
Field pass: doe
File file: Upload any test file.
2-> Observe that the name and pass fields are parsed correctly, but the file field is not parsed (p.File is nil).

cURL

curl -X POST http://localhost:3000/ \
  -F "name=john" \
  -F "pass=doe" \
  -F "[email protected]"

Outputs

file:<nil>
2025/01/18 12:00:00 john
2025/01/18 12:00:00 doe
Name:john - Pass:doe - File:<nil>

Expected Behavior

The BodyParser method should correctly parse all fields in a multipart/form-data request, including files, into the corresponding struct fields (*multipart.FileHeader).

Fiber Version

v2.52.6

Code Snippet (optional)

package main

import (
	"fmt"
	"github.com/gofiber/fiber/v2"
	"log"
	"mime/multipart"
)

type Person struct {
	Name string                `json:"name" xml:"name" form:"name"`
	Pass string                `json:"pass" xml:"pass" form:"pass"`
	File *multipart.FileHeader `json:"file" xml:"file" form:"file"`
}

func main() {
	app := fiber.New()

	app.Post("/", func(c *fiber.Ctx) error {
		p := new(Person)

		if err := c.BodyParser(p); err != nil {
			return err
		}

		//file, _ := c.FormFile("file"). // This place works

		fmt.Printf("file:%v \n", p.File)

		log.Println(p.Name) // "john"
		log.Println(p.Pass) // "doe"

		c.SendString(fmt.Sprintf("Name:%v - Pass:%v - File: %v", p.Name, p.Pass, p.File.Filename))
		return c.SendStatus(fiber.StatusOK)
	})

	app.Listen(":3000")
}

Checklist:

  • I agree to follow Fiber's Code of Conduct.
  • I have checked for existing issues that describe my problem prior to opening this one.
  • I understand that improperly formatted bug reports may be closed without explanation.
Copy link

welcome bot commented Jan 20, 2025

Thanks for opening your first issue here! 🎉 Be sure to follow the issue template! If you need help or want to chat with us, join us on Discord https://gofiber.io/discord

@ReneWerner87
Copy link
Member

https://docs.gofiber.io/api/ctx#bodyparser

Pls test with the multipart form header

@ReneWerner87
Copy link
Member

Image
curl already sends the correct header without specifying it

package main

import (
	"fmt"
	"github.com/gofiber/fiber/v2"
	"log"
	"mime/multipart"
)

type Person struct {
	Name string                `json:"name" xml:"name" form:"name"`
	Pass string                `json:"pass" xml:"pass" form:"pass"`
	File *multipart.FileHeader `json:"file" xml:"file" form:"file"`
}

func main() {
	app := fiber.New()

	app.Post("/", func(c *fiber.Ctx) error {

		fmt.Println("header:", c.Get("Content-Type"))

		p := new(Person)

		if err := c.BodyParser(p); err != nil {
			return err
		}

		//file, _ := c.FormFile("file"). // This place works

		fmt.Printf("file:%v \n", p.File)

		log.Println(p.Name) // "john"
		log.Println(p.Pass) // "doe"

		multi, err := c.MultipartForm()
		if err != nil {
			return err
		}
		fmt.Println("multi:", multi)

		for key, values := range multi.Value {
			for _, value := range values {
				fmt.Println(key, value)
			}
		}

		return c.Status(fiber.StatusOK).JSON(p)
		//return c.SendStatus(fiber.StatusOK)
	})

	app.Listen(":3000")
}

problem is that we only evaluate the value part of the multipart information and feed it into the struct
Image

should we also attach the file part to the struct or is there something against it? currently there are two maps
we do the same in v3 and could change this for both versions
@gaby @efectn @sixcolors

@ReneWerner87
Copy link
Member

v3

for key, values := range multipartForm.Value {

v2

fiber/ctx.go

Line 425 in e04f815

for key, values := range multipartForm.Value {

@SadikSunbul
Copy link
Author

In my opinion, attaching file fields to the struct would improve usability and consistency. It would allow developers to handle both textual fields and file uploads in a unified manner without additional manual steps like c.FormFile("file").

However, I see two considerations here:

  1. Backward Compatibility: If this change affects existing projects that rely on the current behavior, it might cause unexpected issues.
  2. Performance Impact: Parsing and attaching files could add overhead, especially for large uploads.
    A possible approach could be to make this behavior optional by introducing a configuration setting in BodyParser. For example:
app.Post("/", func(c *fiber.Ctx) error {
  c.BodyParser(p, fiber.WithMultipartFileSupport())
})

This way, developers can opt-in for file parsing when needed. What do you think?

@ReneWerner87
Copy link
Member

related #2002

@ReneWerner87 ReneWerner87 removed the v3 label Jan 21, 2025
@ReneWerner87
Copy link
Member

since we have a feature stop in version 2, we will no longer add this there

in version 3 we are working on making this possible
the challenge there is that the schema parser can only use a map of strings to assign this to the struct, which has always been enough until now
so we have to break up the parsing and enable the multipart file to be passed on

We will work on this in the issue #2002

@sixcolors
Copy link
Member

@ReneWerner87. I believe we should aim to make BodyParser handle file fields automatically.

Thoughts:

  1. Simplicity: Developers expect BodyParser to handle both textual fields and files seamlessly. Adding configuration adds unnecessary complexity and deviates from Fiber’s "minimal effort" philosophy.
  2. Backward Compatibility: As long as existing functionality isn’t disrupted, automatically supporting file fields should be the default behavior.
  3. Performance: While there might be slight overhead for parsing files, this is expected when handling multipart/form-data.

For v2, clarifying the current behavior in the docs is helpful, but for v3, I’d recommend prioritizing this improvement to meet developer expectations out of the box.

@ReneWerner87
Copy link
Member

Yeah
For v2 we extend the documentation
And v3 is handling it automatically (#2002)

@ReneWerner87
Copy link
Member

3729281

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants