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

React+Express POST请求卡在中间件问题求助

POST请求卡在Express中间件的排查方案

问题背景

使用React CRA构建前端、Express.js搭建后端,发起POST请求时请求卡在中间件环节。已尝试添加/移除async/await、重启服务器及本地机器,问题仍未解决。

相关代码

后端代码

server.js

var express = require('express');
var app = express();
var cors = require('cors');
const corsOptions = {
  origin: '*',
  credentials: true,
  optionSuccessStatus: 200,
};

app.use(cors(corsOptions));
app.use("/contact", contact);
app.use("/offer", offer);

app.listen(3015);

index.js

var nodemailer = require('nodemailer');
var cors = require('cors');
const creds = require('../../config');
const app = express();
const corsOptions = {
  origin: '*',
  credentials: true,
  optionSuccessStatus: 200,
};

// Multiple routing
const router1 = express.Router();
const router2 = express.Router();

app.use(cors(corsOptions));
app.use(express.json());
app.use('/contact', router1);
app.use('/offer', router2);
app.set('port', 3015);

var transport = {
  host: 'kelvin.superhosting.bg',
  port: 587,
  auth: {
    user: creds.USER,
    pass: creds.PASS
  }
}
var transporter = nodemailer.createTransport(transport)
transporter.verify((error, success) => {
  if (error) {
    console.log(error);
  } else {
    console.log(success);
    // console.log('Server is ready to take messages');
  }
});

exports.contact = () => {
  router1.post('/contact', (req, res, next) => {
    // console.log('req.body', req.body);
    var name = req.body.name;
    var email = req.body.email;
    var subject = req.body.subject;
    var message = req.body.message;
    var content = `name: ${name} \n email: ${email} \n\n message: ${message} `;
    var mail = {
      from: name,
      to: 'sales@pokrivala.net',  // Change to email address that you want to receive messages on
      subject: subject,
      text: content
    }
    transporter.sendMail(mail, (err, data) => {
      if (err) {
        console.log('err', err)
        res.json({
          status: 'fail'
        })
      } else {
        console.log('here');
        res.json({
          status: 'success'
        })
      }
    });
    next();
  });
}

exports.offer = async () => {
  await router2.post('/offer', (req, res, next) => {
    console.log('req.body', req.body);
    var subject = "Hey this is a subject example";
    var mail = {
      to: 'sales@pokrivala.net',  // Change to email address that you want to receive messages on
      subject: subject,
      attachments: [
        {
          filename: `${req.body.attachments[0].filename}`,
          content: `${req.body.attachments[0].content}`,
          encoding: `${req.body.attachments[0].encoding}`,
        },
      ],
    }
    transporter.sendMail(mail, (err, data) => {
      if (err) {
        res.json({
          status: 'fail'
        })
      } else {
        res.json({
          status: 'success'
        })
      }
    });
  });
}

前端代码

index.jsx

async function fetchMessage() {
    const response = await fetch(`http://localhost:3015/contact`, {
      method: "POST",
      body: JSON.stringify(values[0]),
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
    }).then(
      (response) => (response.json())
    ).then((response) => {
      if (response.status === 'success') {
        setValues([]);
        setName('');
        setEmail('');
        setSubject('');
        setMessage('');
        setCaptchaText('');
        onRefresh();
        setMessageOpen(true);
      } else if (response.status === 'fail') {
        console.log("Message failed to send.", response)
      }
    });

    return response;
  }

排查建议

1. 修复路由挂载逻辑

  • server.js中直接挂载contact和offer是错误的,因为这两个是导出的函数,不是Express路由实例。需要先调用函数初始化路由。
  • index.js中不应重复创建Express实例app,这会导致路由没有挂载到server.js的主app上。正确做法是仅导出路由对象,而非创建新的app。

修改示例:

// index.js 调整为只导出路由
const router1 = express.Router();
// ... 保留router1的post逻辑
exports.contactRouter = router1;

// server.js 中挂载
const { contactRouter, offerRouter } = require('./path/to/index');
app.use("/contact", contactRouter);

2. 修正next()调用时机

contact路由中,next()在transporter.sendMail之前调用,而sendMail是异步操作。这会导致请求还未完成响应就交给下一个中间件,引发请求挂起。应删除next()(因为已经用res.json完成了响应),或者将next()放在sendMail的回调函数内。

3. 清理无效的async/await

offer路由中的await对router2.post毫无作用,因为router.post不返回Promise。如果要使用async/await,需将sendMail转换为Promise形式:

router2.post('/offer', async (req, res) => {
  try {
    await transporter.sendMail(mail);
    res.json({ status: 'success' });
  } catch (err) {
    res.json({ status: 'fail' });
  }
});

4. 验证请求体解析

打开index.js中console.log('req.body', req.body)的注释,确认Express的express.json()中间件是否正确解析了前端发送的JSON数据。如果req.body为空,检查前端请求头Content-Type是否正确(已设置application/json,但需确认body是否为有效JSON)。

5. 排查Nodemailer连接问题

检查transporter.verify的输出:

  • 如果打印错误,说明邮箱服务连接失败,需核对host、端口、账号密码是否正确(注意587端口是否需要TLS,可添加secure: false, tls: { rejectUnauthorized: false }测试)。
  • 如果sendMail长时间无响应,可能是邮箱服务器超时,可增加超时配置或更换测试邮箱。

6. 检查端口与跨域

确认server.js监听的是3015端口,前端请求地址http://localhost:3015/contact正确。虽然配置了CORS,但可临时简化corsOptions为app.use(cors())测试是否为跨域配置问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 16:28:15