Nuxt3中使用Nodemailer发送表单遇500错误求助
Nuxt3 + Nodemailer 表单提交500错误排查与修复
核心问题梳理
后端(contact.post.js)错误
- Nodemailer配置拼写错误:
sendMail中的form字段应为from;且html字段引用了不存在的data.message,实际无对应字段。 - 邮箱验证逻辑反转:
if (validator.isEmail(body.email || ''))会在邮箱格式正确时抛出“必填”错误,应改为if (!validator.isEmail(body.email || ''))。 - 评论字段验证逻辑错误:当前逻辑是“评论不为空就报错”,且错误信息不完整,而前端标注评论为可选,需删除该验证。
- Async/Await与Promise混用:嵌套
then/catch导致错误捕获不清晰,改用统一try/catch更易维护。
前端(form.vue)错误
- 邮箱字段v-model绑定错误:输入框绑定
form.mail,但form对象定义的是email,导致邮箱值无法正确提交。 - $fetch请求格式错误:无需手动
JSON.stringify,直接传递form.value即可,否则后端readBody可能解析失败。 - 重复绑定提交事件:form标签和按钮同时绑定提交事件,导致重复请求,需移除按钮的
@click。 - 参数传递冗余:submit函数无需接收form参数,直接使用定义好的ref对象即可。
修正后的代码
后端contact.post.js
import nodemailer from 'nodemailer'; import validator from 'validator'; const config = useRuntimeConfig(); const transporter = nodemailer.createTransport({ host: config.MAILHOST, port: config.MAILPORT, auth: { user: config.MAILUSER, pass: config.MAILPASSWORD, }, }); export default defineEventHandler(async (event) => { try { const body = await readBody(event); const data = await isValid(body); const mail = await transporter.sendMail({ from: `"${data.name}" <${data.email}>`, // 修正form为from to: config.CONTACTMAIL, subject: `来自${data.name}的咨询`, // 调整为合理主题 text: `姓名:${data.name}\n电话:${data.phone}\n邮箱:${data.email}\n预算:${data.budget}\n评论:${data.comment}`, html: `<p>姓名:${data.name}</p><p>电话:${data.phone}</p><p>邮箱:${data.email}</p><p>预算:${data.budget}</p><p>评论:${data.comment || '无'}</p>`, }); console.log('Message sent: %s', mail.messageId); console.log('Preview URL: %s', nodemailer.getTestMessageUrl(mail)); return '发送成功!'; } catch (error) { console.error('发送失败:', error); sendError(event, createError({ statusCode: error.name === 'ValidationError' ? 400 : 500, statusMessage: Array.isArray(error) ? error.map(e => e.error).join(', ') : error.message })); } }); async function isValid(body) { const errors = []; if (validator.isEmpty(body.name || '')) errors.push({ field: 'name', error: '姓名为必填项。' }); if (validator.isEmpty(body.phone || '')) errors.push({ field: 'phone', error: '电话为必填项。' }); if (!validator.isEmail(body.email || '')) // 修正验证逻辑 errors.push({ field: 'email', error: '请输入有效的邮箱地址。' }); if (validator.isEmpty(body.budget || '')) errors.push({ field: 'budget', error: '预算为必填项。' }); if (errors.length > 0) { const validationError = new Error('验证失败'); validationError.name = 'ValidationError'; validationError.details = errors; throw validationError; } else { return { email: validator.normalizeEmail(body.email), name: validator.escape(body.name), phone: validator.escape(body.phone), budget: validator.escape(body.budget), comment: validator.escape(body.comment || ''), }; } }
前端form.vue
<template> <div class="m-3 lg:m-1"> <form @submit.prevent="submit"> <div id="form" class="flex tad"> <div class="forma w-1/3 m-8"> <h1 class="text-volna send text-4xl mb-3">Оставь заявку</h1> <div> <p class="text-white">Ваше ФИО</p> <input v-model="form.name" name="name" class="inp" type="text" placeholder="введите ваше имя" /> <p class="text-white">Ваш номер телефона</p> <input v-model="form.phone" name="phone" class="inp" type="text" placeholder="Ввведите номер для связи с вами" /> <p class="text-white">Ваш email</p> <input name="email" v-model="form.email" <!-- 修正绑定字段 --> class="inp" type="email" placeholder="введите контактный email" /> </div> </div> <div class="forma w-1/3 m-8"> <div class="sec_forma"> <div> <p class="text-white">Ваш бюджет</p> <input v-model="form.budget" name="money" class="inp" type="text" placeholder="введите ваш бюджет" /> <p class="text-white">Введите Ваш комментарий (по желанию)</p> <input v-model="form.comment" name="comment" class="inp" type="text" placeholder=" ваш комментарий" /> <button type="submit" <!-- 移除重复点击事件 --> class="button button-1 mb-3" > отправить </button> </div> </div> </div> </div> </form> </div> </template> <script setup> const form = ref({ name: '', phone: '', email: '', budget: '', comment: '', }); const errors = ref(false); const success = ref(false); const waiting = ref(false); async function submit() { waiting.value = true; try { await $fetch('/api/contact', { // 用相对路径避免硬编码 method: 'POST', body: form.value, // 直接传递对象 }); errors.value = false; success.value = true; form.value = { name: '', phone: '', email: '', budget: '', comment: '', }; } catch (err) { errors.value = true; success.value = false; console.error('提交失败:', err); } finally { waiting.value = false; } } </script>
额外排查建议
- 检查
nuxt.config.ts中的环境变量,确保MAILHOST、MAILPORT、MAILUSER、MAILPASSWORD、CONTACTMAIL已正确配置(服务器端变量需加NUXT_前缀)。 - 启动Nuxt时查看终端日志,后端错误日志会明确显示具体问题(如邮箱服务器连接失败、认证错误等)。
- 用Postman测试时,携带完整请求体(包含name、phone、email、budget字段),查看返回的错误详情定位问题。
内容的提问来源于stack exchange,提问作者forr
相关产品推荐
相关产品推荐

