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中间件
- 安装multer依赖:
npm install multer
- 修改后端路由文件,添加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
相关产品推荐
相关产品推荐

