Express表单提交req.body为空问题求助(基于Nodemailer)
解决Nodemailer表单提交时req.body为空的问题
我来帮你排查这个问题,看起来有几个关键的点导致了req.body为空,咱们一步步来解决:
核心问题分析
- 表单包含文件上传,却没设置正确的编码类型:你的表单里有CV文件上传字段,但默认的表单编码
application/x-www-form-urlencoded无法处理文件,必须用multipart/form-data,而body-parser不支持解析这种类型的请求体。 - 表单字段命名与路由不匹配:你路由里用
req.body.name、req.body.email,但表单里的字段名是job_form[role]、job_form[email],就算拿到请求体也会出现变量未定义的问题。 body-parser无法处理multipart/form-data:这个中间件只负责解析application/x-www-form-urlencoded和application/json类型的请求,对文件上传无能为力。
解决方案步骤
1. 修改表单的编码类型
首先给你的表单加上enctype="multipart/form-data"属性,确保能正确传输文件和表单数据:
<form method="POST" action="/jobs/join-us" enctype="multipart/form-data"> <div class='input-wrapper'> <input class="flying-label-input" type="text" name="job_form[role]" id="job_form_role" /> <label class="flying-label required" for="job_form_role">Role</label> </div> <div class='input-wrapper'> <input class="flying-label-input" type="text" name="job_form[email]" id="job_form_email" /> <label class="flying-label required" for="job_form_email">E-mail</label> </div> <div class='input-wrapper'> <input class="flying-label-input" type="text" name="job_form[phone_number]" id="job_form_phone_number" /> <label class="flying-label" for="job_form_phone_number">Phone number</label> </div> <div class='input-wrapper'> <label class="label required" for="job_form_cv">CV (PDF)</label> <input type="file" name="job_form[cv]" id="job_form_cv" /> </div> <div class='input-wrapper-space-top'> <input type="hidden" name="job_form[referer]" id="job_form_referer" /> <input type="submit" name="commit" value="Submit Job Application" class="btn-round btn-primary" /> </div> </form>
2. 替换body-parser为multer
multer是专门处理multipart/form-data的Express中间件,既能解析普通表单字段,也能处理文件上传。
首先安装multer:
npm install multer
然后修改你的app.js中间件配置:
const express = require('express') var app = express(); var path = require('path') const nodemailer = require('nodemailer'); // 引入multer const multer = require('multer'); // 配置multer为内存存储(如果需要保存文件到服务器磁盘,可以修改为diskStorage) const storage = multer.memoryStorage(); const upload = multer({ storage: storage }); // 静态文件服务保留 app.use(express.static('public')) // 保留JSON解析(如果有其他JSON请求的话) app.use(express.json()) // 在路由中使用upload中间件,upload.any()会处理所有字段(包括文件) app.post('/jobs/join-us', upload.any(), (req, res) => { // 现在req.body包含普通表单字段,req.files包含上传的文件 console.log('表单字段:', req.body); console.log('上传的文件:', req.files); // 解析job_form[xxx]格式的字段为嵌套对象 const jobForm = {}; Object.keys(req.body).forEach(key => { if (key.startsWith('job_form[')) { const fieldName = key.slice(9, -1); // 提取role、email等字段名 jobForm[fieldName] = req.body[key]; } }); console.log('解析后的表单数据:', jobForm); // 构建邮件内容,注意字段名对应(路由里原来的name对应表单里的role) const output = ` <p>You have a new job application from the contact form.</p> <h3>Application Details</h3> <ul> <li>Applied Role: ${jobForm.role}</li> <li>Email: ${jobForm.email}</li> <li>Phone Number: ${jobForm.phone_number || 'Not provided'}</li> </ul> `; // Nodemailer配置不变 let transporter = nodemailer.createTransport({ host: 'smtp.gmail.com', port: 587, secure: false, // true for 465, false for other ports auth: { user: 'xx', // 你的邮箱 pass: 'xx', // 你的邮箱密码/应用密码 }, tls: { rejectUnauthorized: false } }); // 如果需要把CV作为附件发送,添加attachments选项 let mailOptions = { from: 'xx', // 发件人 to: 'xx', // 收件人 subject: 'New Job Application Received', text: 'A new job application has been submitted.', html: output, attachments: req.files.map(file => ({ filename: file.originalname, content: file.buffer })) }; // 发送邮件 transporter.sendMail(mailOptions, (error, info) => { if (error) { console.error('邮件发送失败:', error); return res.status(500).send("Failed to send application"); } console.log('Message sent: %s', info.messageId); res.sendStatus(200); }); });
3. 额外注意事项
- 字段名对应:一定要确保路由里使用的字段名和表单里的一致,我在示例里把原来的
name改成了role,因为你的表单里是job_form[role]。 - Gmail应用密码:如果你用Gmail作为SMTP服务器,现在需要用应用密码代替普通密码,尤其是开启了两步验证的账号,否则会认证失败。
- 文件存储选项:如果不想把文件存在内存里,可以用
multer.diskStorage()配置磁盘存储路径,方便后续管理。
内容的提问来源于stack exchange,提问作者Mikk Küttim
相关产品推荐
相关产品推荐

