node-formidable / formidable

The most used, flexible, fast and streaming parser for multipart form data. Supports uploading to serverless environments, AWS S3, Azure, GCP or the filesystem. Used in production.
MIT License
7.04k stars 682 forks source link

Parsing multipart form data not working with Next.js #868

Closed kylecombes closed 2 years ago

kylecombes commented 2 years ago

Support plan

Context

What are you trying to achieve or the steps to reproduce?

I am trying to handle the following multipart form data submission:

--part-delimiter
Content-Disposition: form-data; name="santa-claus"
Content-Type: application/json
{
    "collector": "Santa Claus",
    "collectedAt": "2022-02-17T17:17:55.764Z",
    "latitude":90,
    "longitude":0
}
--part-delimiter
Content-Disposition: form-data; name="mrs-claus"
Content-Type: application/json
{
    "collector": "Mrs Claus",
    "collectedAt": "2022-03-17T17:17:55.764Z",
    "latitude":90,
    "longitude":1
}
--part-delimiter--

The request is being sent with Postman and includes the header Content-Type: multipart/related; boundary=part-delimiter.

I am using this code to handle the request:

export default async function (req: NextApiRequest, res: NextApiResponse) {
  switch (req.method) {
    case 'POST':
      const form = formidable({});
      form.parse(req, (error, fields, files) => {
        if (error) {
          console.error(error);
          res.status(500).end();
          return;
        }
        console.log({ fields, files });
        res.status(200).end();
      });
    break;
  }
}

(NextApiRequest extends IncomingMessage, so the types are compatible)

What was the result you got?

The callback passed to form.parse is never called.

I've tried setting breakpoints to see what is going wrong, but I'm unable to figure it out. It appears that the data is being processed because this.write(buffer) is called (and the catch below doesn't catch any errors). However, while initMultipart is called and a callback is registered to handle processing data (these lines are running), the callback is never called (i.e. a breakpoint set at the first line of the callback is never hit).

Eventually, when Formidable finishes processing the stream and gets to the check if (!this.ended || this._flushing || this.error), this.ended is undefined and thus the function returns and the parser is never informed that the parsing is done. (I tried removing the !this.ended check to see if maybe that just wasn't set by the parser. This does lead the callback passed to form.parse to be called, but fields and files are then empty objects.)

What result did you expect?

I expected it to handle parsing the form and call the callback provided to form.parse, or at least throw an error.

GrosSacASac commented 2 years ago

Could you try again with version 3 ?

Why are you using multipart/related instead of multipart/form-data ?

Taaitaaiger commented 2 years ago

I'm running into the same problem, I've tried with version 2 and 3. In neither case the callback is called. It is an application/json request rather than a multipart/form-data, I'll check tomorrow if the problem persists with with the other kind of request, but I never had this problem when I just used node's http module (which I want to replace with express).

Edit: Works as expected with multipart/form-data

kylecombes commented 2 years ago

@GrosSacASac I just tried again with version 3 and using multipart/form-data, but I still experience the same issue.

kylecombes commented 2 years ago

Well, I figured it out: Turns out the problem was Next.js automatically parsing the request body stream. The fix is simply to add the following to the end of my Next.js API route file:

export const config = {
  api: {
    bodyParser: false,
  },
};

I realized it had to be Next.js when I tried using the example code from the repo and it stopped working as soon as I involved Next.js. I feel like an idiot because this auto-parsing has been a problem for me in the past so I was aware of this behavior. But it's fixed now!

Freundschaft commented 2 years ago

is there a way to somehow parse the json content directly as string and not copy it into a file fron formidable's side?

GrosSacASac commented 2 years ago

Yes by using the fileWriteStreamHandler option. And in the function that returns a writestream, check for {mimetype} and do something when it includes json

Freundschaft commented 2 years ago

ok cool, i was just wondering if there was some built in parser already, but thanks :)