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

Node/Express服务接收fetch请求超时,跨域报错求助

Node/Express邮件服务CORS超时问题排查与修复

问题描述

基于Node/Express搭建邮件发送服务,通过nodemailer处理前端表单的fetch请求。点击提交后请求超时,浏览器报错:No 'Access-Control-Allow-Origin' header is present on the requested resource。已在服务端引入并配置cors(尝试过无选项、通配符"*"及指定站点源),邮件能正常发送,但客户端无法接收成功状态码。

原服务端代码

const express = require('express')
const http = require('http')
const cors = require('cors')
require('dotenv').config()
const mailer = require('nodemailer')


const app = express()

app.use(express.json())
app.use(express.urlencoded({extended: true}))

app.use(cors({
    origin: "*"
}))



//email middleware



const transporter = mailer.createTransport({
    service: "gmail",
    auth: {
        user: process.env.GAU,
        pass: process.env.GAP
    }
})

async function sendEmail(emailAddress, emailSubject, emailBody, from="site"){
    const options = { timeZone: 'America/Chicago', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' };
    const currentDate = new Date().toLocaleString('en-US', options);
    let message = "<h1>"+emailSubject+"</h1>"
    message += "<p>Email from contact form on myname."+from+"</p>"
    message += "<br><br>"
    message += "<h2>" + emailBody + "</h2>"
    message += "<br><br>"
    message += "<p>Reply to: "+emailAddress+"</p>"
    message += "<br><br>"
    message += "<p>At: "+currentDate+"</p>"
    const mailOptions = {
        form: process.env.GAU,
        to: process.env.CONTACT_EMAIL,
        subject: emailSubject + " | Contact Form",
        html: message
    }
    const mailOptions2 = {
        form: process.env.GAU,
        to: emailAddress,
        subject: "Thank you for reaching out!",
        html: "<em>Thank you for reaching out!</em><br><br>"+message
    }
    let successful = true
    transporter.sendMail(mailOptions, (error, info)=>{
        if(error){
            console.log('Error', error)
            successful = false
        }
        else{
            console.log("Email sent: ", info.response)
        }
    })
    transporter.sendMail(mailOptions2, (error, info)=>{
        if(error){
            console.log('Error', error)
            successful = false
        }
        else{
            console.log("Email sent: ", info.response)
        }
    })
    return successful
}



//routes

app.get('/', (req,res)=>{
    res.json('test ok')
})

app.post("/sendEmailFromSiteForm",(req,res)=>{
    const {
        emailAddress, emailSubject, emailBody
    } = req.body
    try {
        let result = sendEmail(emailAddress, emailSubject, emailBody, "site")
        if(result) res.status(200)
        else res.status(500)
    } catch (error) {
        res.status(500)
    }
})


app.post("/sendEmailFromWorkForm",(req,res)=>{
    const {
        emailAddress, emailSubject, emailBody
    } = req.body
    try {
        let result = sendEmail(emailAddress, emailSubject, emailBody, "work")
        if(result) res.status(200)
        else res.status(500)
    } catch (error) {
        res.status(500).json(error)
    }
})

app.listen(8080 , ()=>{
    'Server started on port 8080.'
})

原客户端代码

async function sendEmail(address, subject, body){
    try {
        let response = await fetch('mysiteURL',{
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                emailAddress: address,
                emailSubject: subject,
                emailBody: body
            })
        })
        
        if(response.ok){
            console.log('email sent okay')
        } else{
            console.log('Email failed to send.')
        }
        console.log(response.json())

    } catch (error) {
        console.log('Email failed to send.')
    }
}

问题根源与修复方案

1. 响应未发送(核心超时原因)

服务端路由仅设置了状态码res.status(200)/res.status(500),但未调用send()或json()发送响应,导致请求一直挂起超时,浏览器触发CORS错误(超时后浏览器默认抛出该错误,实际根源是无响应返回)。

2. 异步函数未等待

sendEmail是async函数,但路由调用时未加await,导致result为Promise对象而非布尔值,if(result)永远为true,且邮件发送逻辑未完成就提前执行响应判断。

3. nodemailer回调未正确处理异步

sendEmail中使用回调式transporter.sendMail,未等待两个邮件发送完成就返回successful,返回值不准确。改用await处理nodemailer的Promise支持。

4. 拼写错误

邮件配置中的form应为from,否则会导致邮件发送的发件人配置错误。

修复后的代码

服务端核心修复代码

// 重构sendEmail函数,用await处理异步
async function sendEmail(emailAddress, emailSubject, emailBody, from="site"){
    const options = { timeZone: 'America/Chicago', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' };
    const currentDate = new Date().toLocaleString('en-US', options);
    let message = "<h1>"+emailSubject+"</h1>"
    message += "<p>Email from contact form on myname."+from+"</p>"
    message += "<br><br>"
    message += "<h2>" + emailBody + "</h2>"
    message += "<br><br>"
    message += "<p>Reply to: "+emailAddress+"</p>"
    message += "<br><br>"
    message += "<p>At: "+currentDate+"</p>"
    const mailOptions = {
        from: process.env.GAU, // 修复form拼写错误为from
        to: process.env.CONTACT_EMAIL,
        subject: emailSubject + " | Contact Form",
        html: message
    }
    const mailOptions2 = {
        from: process.env.GAU, // 修复form拼写错误为from
        to: emailAddress,
        subject: "Thank you for reaching out!",
        html: "<em>Thank you for reaching out!</em><br><br>"+message
    }
    try {
        // 等待两个邮件发送完成
        await transporter.sendMail(mailOptions);
        await transporter.sendMail(mailOptions2);
        return true;
    } catch (error) {
        console.log('Error sending email:', error);
        return false;
    }
}

// 修复路由,添加async/await并发送响应
app.post("/sendEmailFromSiteForm", async (req,res)=>{
    const { emailAddress, emailSubject, emailBody } = req.body
    try {
        let result = await sendEmail(emailAddress, emailSubject, emailBody, "site")
        if(result) {
            res.status(200).json({ message: "邮件发送成功" });
        } else {
            res.status(500).json({ message: "邮件发送失败" });
        }
    } catch (error) {
        console.error(error);
        res.status(500).json({ message: "服务器错误", error: error.message });
    }
})

app.post("/sendEmailFromWorkForm", async (req,res)=>{
    const { emailAddress, emailSubject, emailBody } = req.body
    try {
        let result = await sendEmail(emailAddress, emailSubject, emailBody, "work")
        if(result) {
            res.status(200).json({ message: "邮件发送成功" });
        } else {
            res.status(500).json({ message: "邮件发送失败" });
        }
    } catch (error) {
        console.error(error);
        res.status(500).json({ message: "服务器错误", error: error.message });
    }
})

// 修复日志输出
app.listen(8080 , ()=>{
    console.log('Server started on port 8080.')
})

客户端优化代码

async function sendEmail(address, subject, body){
    try {
        let response = await fetch('mysiteURL',{
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                emailAddress: address,
                emailSubject: subject,
                emailBody: body
            })
        })
        
        if(response.ok){
            const data = await response.json(); // 等待json解析完成
            console.log('email sent okay:', data.message);
        } else{
            const errorData = await response.json();
            console.log('Email failed to send:', errorData.message);
        }

    } catch (error) {
        console.log('请求失败:', error.message);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 23:52:06