You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MERN栈联系表单后端接收值为undefined(SendGrid邮件异常)

问题描述

基于MERN技术栈开发的联系表单,前端能正常发送请求并命中后端路由,但后端req.body中的name、email、message均为undefined。使用SendGrid发送邮件时,邮件能成功发送,但相关字段显示为undefined。

已尝试更换表单样式、直接用axios发起请求、打印请求信息确认前端数据正常发送,但问题仍未解决。


相关代码

表单组件

import { useState } from 'react'
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import ContactService from '../services/contact.service';
import {
  Button,
  FormControl,
  Container,
  CircularProgress,
  OutlinedInput,
  Paper,
  TextField,
  InputAdornment,
  Box,
  Typography,
  ImageList,
  ImageListItem,
} from "@mui/material";


const ContactForm = () => {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    message: '',
  });
  const navigate = useNavigate();

  const handleChange = (e) => {
    setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value }));
    console.log(formData)
  };

  async function handleSubmit(e, from='/') {
    e.preventDefault();
    const contactForm = new FormData();

    contactForm.append("name", formData.name);
    contactForm.append("message", formData.message);
    contactForm.append("email", formData.email);
    await ContactService.contact(contactForm);
    navigate(from, {
      state: {
        type: "success",
        message: `Message sent!`,
      },
      replace: true,
    });
  };


    return (
        <>
     <div class="flex justify-center items-center w-screen h-screen bg-white">

    <div class="container mx-auto my-4 px-4 lg:px-20">

        <div class="w-full p-8 my-4 md:px-12 lg:w-9/12 lg:pl-20 lg:pr-40 mr-auto rounded-2xl shadow-2xl">
            <div class="flex">
                <h1 class="font-bold uppercase text-5xl">Send us a <br /> message</h1>
            </div>
            <div class="grid grid-cols-1 gap-5 md:grid-cols-2 mt-5">
                <input class="w-full bg-gray-100 text-gray-900 mt-2 p-3 rounded-lg focus:outline-none focus:shadow-outline"
            type="text" placeholder="First Name*" name='name' onChange={handleChange}/>
                <input class="w-full bg-gray-100 text-gray-900 mt-2 p-3 rounded-lg focus:outline-none focus:shadow-outline"
            type="email" placeholder="Email*" name='email' onChange={handleChange}/>
                <input class="w-full bg-gray-100 text-gray-900 mt-2 p-3 rounded-lg focus:outline-none focus:shadow-outline"
            type="number" placeholder="Phone*" />
        </div>
                <div class="my-4">
                    <textarea name='message' onChange={handleChange} placeholder="Message*" class="w-full h-32 bg-gray-100 text-gray-900 mt-2 p-3 rounded-lg focus:outline-none focus:shadow-outline"></textarea>
                </div>
                <div class="my-2 w-1/2 lg:w-1/4">
                    <button onClick={handleSubmit} class="uppercase text-sm font-bold tracking-wide bg-blue-900 text-gray-100 p-3 rounded-lg w-full 
                      focus:outline-none focus:shadow-outline">
            Send Message
          </button>
                </div>
            </div>

            <div
                class="w-full lg:-mt-96 lg:w-2/6 px-8 py-12 ml-auto bg-blue-900 rounded-2xl">
                <div class="flex flex-col text-white">
                    <h1 class="font-bold uppercase text-4xl my-4">Drop in our office</h1>
                    <p class="text-gray-400">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam
                        tincidunt arcu diam,
                        eu feugiat felis fermentum id. Curabitur vitae nibh viverra, auctor turpis sed, scelerisque ex.
                    </p>

                    <div class="flex my-4 w-2/3 lg:w-1/2">
                        <div class="flex flex-col">
                            <i class="fas fa-map-marker-alt pt-2 pr-2" />
            </div>
            <div class="flex flex-col">
              <h2 class="text-2xl">Main Office</h2>
              <p class="text-gray-400">5555 Tailwind RD, Pleasant Grove, UT 73533</p>
            </div>
          </div>
          
          <div class="flex my-4 w-2/3 lg:w-1/2">
            <div class="flex flex-col">
              <i class="fas fa-phone-alt pt-2 pr-2" />
            </div>
            <div class="flex flex-col">
              <h2 class="text-2xl">Call Us</h2>
              <p class="text-gray-400">Tel: xxx-xxx-xxx</p>
              <p class="text-gray-400">Fax: xxx-xxx-xxx</p>
            </div>
          </div>
          
          <div class="flex my-4 w-2/3 lg:w-1/2">
            <a href="#" target="_blank" rel="noreferrer" class="rounded-full bg-white h-8 w-8 inline-block mx-1 text-center pt-1">
              <i class="fab fa-facebook-f text-blue-900" />
            </a>
            <a href="#" target="_blank" rel="noreferrer" class="rounded-full bg-white h-8 w-8 inline-block mx-1 text-center pt-1">
              <i class="fab fa-linkedin-in text-blue-900" />
            </a>
          </div>
        </div>
      </div>
    </div>

</div>


<div class="flex items-end justify-end fixed bottom-0 right-0 mb-4 mr-4 z-10">
  <div>
    <a title="Buy me a pizza" href="#" target="_blank" class="block w-16 h-16 rounded-full transition-all shadow hover:shadow-lg transform hover:scale-110 hover:rotate-12">
      <img class="object-cover object-center w-full h-full rounded-full" src="https://img.icons8.com/emoji/48/000000/pizza-emoji.png"/>
    </a>
  </div>
</div>
        </>
    )
}

export default ContactForm

服务层代码

import http from "../utils/http-common";

class ContactService {
    contact(formData) {
        return http.post("/contact", formData);
      }
}

export default new ContactService();

http-common配置

import axios from "axios";

var url;
process.env.NODE_ENV === "production"
  ? (url = "")
  : (url = "http://localhost:5000");
export default axios.create({
    baseURL: url,
    headers: {
        'Content-Type': 'application/json',
    },
    withCredentials: true,
});

后端路由代码

module.exports.contact = async (req, res, next) => {
    const { email, name, message } = req.body;
    console.log(req.body, req.params)
   
    const sgMail = require('@sendgrid/mail')
    sgMail.setApiKey(process.env.SENDGRID_API_KEY)
    const msg = {
      to: 'test@example.com', // Change to your recipient
      from: 'test@example.com', // Change to your verified sender
      subject: `${email}`,
      text: `${message}`,
      html: `<p>${message}</p>`,
    }
    sgMail
      .send(msg)
      .then(() => {
        console.log('Email sent')
      })
      .catch((error) => {
        console.error(error)
      })
}

解决方案

核心问题是请求数据格式与后端解析逻辑不匹配:前端用FormData传递数据,但请求头被强制设置为application/json,同时后端未配置解析multipart/form-data的中间件,导致后端无法正确解析请求体。

提供两种修复方案:

方案1:直接发送JSON格式数据(推荐)

不需要使用FormData,直接传递formData对象,保持请求头为application/json,后端用默认的express.json()中间件即可解析。

修改表单组件的handleSubmit方法

async function handleSubmit(e, from='/') {
  e.preventDefault();
  // 移除FormData构造,直接发送formData对象
  await ContactService.contact(formData);
  navigate(from, {
    state: {
      type: "success",
      message: `Message sent!`,
    },
    replace: true,
  });
};

方案2:保留FormData,适配请求头与后端解析

如果必须使用FormData,需要修改请求头并在后端配置解析multipart/form-data的中间件。

步骤1:修改http-common配置,移除强制的Content-Type

axios会自动根据FormData设置正确的Content-Type: multipart/form-data,无需手动指定:

import axios from "axios";

var url;
process.env.NODE_ENV === "production"
  ? (url = "")
  : (url = "http://localhost:5000");
export default axios.create({
    baseURL: url,
    // 移除手动设置的Content-Type
    withCredentials: true,
});

步骤2:后端安装并配置multer中间件

  1. 安装multer依赖:
npm install multer
  1. 修改后端路由文件,添加multer解析配置:
const multer = require('multer');
// 配置multer,仅解析表单数据不存储文件
const upload = multer();

module.exports.contact = upload.none()(async (req, res, next) => {
    const { email, name, message } = req.body;
    console.log(req.body)
   
    const sgMail = require('@sendgrid/mail')
    sgMail.setApiKey(process.env.SENDGRID_API_KEY)
    const msg = {
      to: 'test@example.com', // 修改为你的收件人邮箱
      from: 'test@example.com', // 修改为你的已验证发件人邮箱
      subject: `来自${name}的消息 - ${email}`,
      text: message,
      html: `<p>发送人:${name}</p><p>邮箱:${email}</p><p>消息:${message}</p>`,
    }
    try {
      await sgMail.send(msg);
      console.log('Email sent');
      res.status(200).json({ message: '邮件发送成功' });
    } catch (error) {
      console.error(error);
      res.status(500).json({ error: '邮件发送失败' });
    }
})

同时确保你的Express应用已配置基础解析中间件:

const express = require('express');
const app = express();

// 解析JSON格式请求体
app.use(express.json());
// 解析urlencoded格式请求体(可选)
app.use(express.urlencoded({ extended: true }));

内容的提问来源于stack exchange,提问作者Isaiahm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 16:01:29