Breldan
Breldan

Reputation: 73

Request with header {"Content-Type": "multipart/form-data;"} sends empty req.body

I'm trying to save the formData below to MongoDB. In the payment below, "attachments" is a FileList.

What's happening is the response in the create controller returns an empty body when the header content type is multipart/form-data. Otherwise, the create controller adds all the fields but not attachments. That returns an empty array.

What could I do on my controller so that I can create the payment with the attachment?

const handlePaymentSubmit = async (e) => {
    e.preventDefault();
    let payment = new FormData();
    payment.append("payee", user.id);
    payment.append("amount", paymentAmount);
    payment.append("code", paymentCode);
    payment.append("method", paymentMethod);
    payment.append("applicationId", applicationId);
    payment.append("attachments", attachments);
    await createPayment(payment);
    {
      isSuccess && dispatch(reset());
    }
  };

In createPayment RTK Endpoint I set the headers content-type to multipart form data

createPayment: builder.mutation({
      query: (payment) => ({
        url: "/api/payments/",
        method: "POST",
        body: payment,
        headers: {
          "Content-Type": "multipart/form-data;",
        },
      }),

On the backend, I set how to store the files in middleware with Multer.

import multer from "multer";

var storeImage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "uploads/payments");
  },
  filename: function (req, file, cb) {
    let ext = file.originalname.substring(file.originalname.lastIndexOf("."));
    let name = file.originalname.substring(0, file.originalname.indexOf("."));
    cb(null, name + "-" + Date.now() + ext);
  },
});

export const storage = multer({
  storage: storeImage,
  fileFilter: function (req, file, cb) {
    if (
      (file.mimetype =
        "image/png" || "image/jpg" || "image/jpeg" || "application/pdf")
    ) {
      cb(null, true);
    } else {
      console.log("Only pdf, jpg and Png Supported");
      cb(null, false);
    }
  },
  limits: {
    fileSize: 1024 * 1024 * 2,
  },
});

And create the payment in my create controller.

import asyncHandler from "express-async-handler";
import formidable from "formidable";
import Payment from "../../models/payment.js";

export const createPayment = asyncHandler(async (req, res) => {
  const { payee, amount, code, method, applicationId } = await req.body;

  if (!payee || !amount || !code || !method || !applicationId) {
    res.status(400);
    throw new Error("Please add all required fields");
  }

  if (req.files) {
    let path = "";
    req.files.forEach(function (files, index, arr) {
      path = path + files.path + ",";
    });
    path = path.substring(0, path.lastIndexOf(","));
  }

  const attachments = req.files.map((attachment) => ({
    path: attachment.path,
    name: attachment.filename,
    size: `${(attachment.size / 1000).toFixed(2).toString()} KBs`,
  }));

  const payment = await Payment.create({
    payee,
    amount,
    code,
    method,
    applicationId,
    attachments,
  });

  res.status(200).json(payment);
});

Upvotes: 4

Views: 3743

Answers (1)

Hadi
Hadi

Reputation: 104

"Content-Type": "multipart/form-data;" this won't work in rtk. By default, when you use RTK Mutation to send data, it will automatically serialize the data into a string format before sending it over the network. However, if you want to send form data without stringifying it, you can use the formData option.

Here's an example of how you can use the formData option in an RTK Mutation query:

createPayment: builder.mutation({
      query: (payment) => ({
        url: "/api/payments/",
        method: "POST",
        body: payment,
        formData: true
      }),

In this example, the createPayment mutation sends form data to the server without stringifying it. The formData option is set to true, which tells RTK Mutation not to serialize the data.

Note that when using the formData option, the Content-Type header will automatically be set to multipart/form-data, which is the standard format for sending form data.

Upvotes: 5

Related Questions