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

NodeJS Express MVC架构下密码重置接口报错TypeError排查

解决密码重置路由中的TypeError: cannot read property 'json' of undefined及邮箱传递问题

你的问题主要由三个核心错误导致:请求格式不规范、Service参数传递错误,以及异步逻辑处理不当。咱们一步步拆解修复:

1. React前端请求未正确传递JSON格式数据

你当前的fetch请求直接把this.state.email作为body发送,但后端Express默认解析的是JSON格式数据,而且你没设置请求头,导致后端req.body是空的,自然拿不到email值。

修复方法:
修改fetch请求,添加Content-Type头并将数据包装为JSON对象:

// 修改sendEmail里的fetch部分
const rawResponse = await fetch(process.env.REACT_APP_SERVER_URI + '/user/password-reset', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json', // 必须添加这个请求头
  },
  body: JSON.stringify({ email: this.state.email }) // 将邮箱包装为JSON格式
});

2. Service函数参数传递完全错误

你的passwordResetService定义的参数是(email,res, req),但Controller调用时只传了req.body.email,而且参数顺序搞反了(Express惯例是req在前、res在后)。这直接导致Service里的res是undefined,调用res.json()时就会抛出那个类型错误。

修复方法:

第一步:调整Service的参数结构

把Service改为接收req和res,从请求对象里获取邮箱,同时统一用await处理异步操作:

// 修改Password Reset Service代码
exports.passwordResetService = async function(req, res){
  try{
    const email = req.body.email;
    // 用await替代then,避免回调地狱
    const user = await User.findOne({ email: email });
    if(!user){
      return res.json('email not in db');
    }
    const token = crypto.randomBytes(20).toString('hex');
    console.log(token);
    // 用await确保用户数据更新写入数据库
    await User.updateOne(
      { _id: user._id },
      { resetPasswordToken: token, resetPasswordExpires: Date.now() + 360000 }
    );
    // 邮件发送配置保持不变
    const transporter = nodemailer.createTransport({
      service: 'gmx',
      host: 'mail.gmx.com',
      port: 587,
      secure: false, // 587端口对应secure:false,465才用true
      tls: { rejectUnauthorized: false }, // 避免开发环境SSL验证错误
      auth: {
        user: process.env.EMAIL_ADDRESS,
        pass: process.env.EMAIL_PASSWORD,
      },
    });
    const mailOptions = {
      from:'testaccount9909@gmx.com',
      to: user.email,
      subject: `Password Reset Email`,
      text: `Please click the following link to reset your account's password:\n`+
      `http://localhost:3003/reset/${token}\n\n`+
      `If you didn't request this, you can ignore this email and your password will remain unchanged!`
    }
    console.log('sending email...');
    transporter.sendMail(mailOptions, function(err, response){
      if(err){
        console.error('there was an error: ', err);
        return res.status(500).json('failed to send email');
      } else {
        console.log('email sent successfully:', response);
        res.status(200).json('recovery email sent');
      }
    })
  }catch(e){
    throw Error("An error occurred while resetting the password");
  }
}

第二步:修改Controller的调用方式

Controller直接把req和res传给Service即可,不需要额外处理返回值(Service已经直接响应客户端):

// 修改Password Reset Controller代码
exports.passwordReset = async function(req, res, next){
  try{
    await UserService.passwordResetService(req, res);
  }catch(e){
    console.log(e, "Oops, password reset error!");
    return res.status(400).json({ 
      status:400, 
      error:true, 
      message:"Failed to send password reset email" 
    });
  }
};

3. 异步逻辑优化

  • 避免在async函数里混用.then(),统一用await让代码更易读、错误处理更统一;
  • 之前的user.update()没有等待操作完成,改用await User.updateOne()确保用户数据更新成功。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:18:55