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

Node.js无数据库Web应用OTP验证接口404及JSON解析错误排查

无数据库OTP登录Web应用的404与JSON解析错误修复

问题诊断

  1. 404错误原因:前端发起OTP验证请求的路径是/verify-otp,但后端未定义该接口,仅通过/login处理验证,路径不匹配导致请求失败。
  2. JSON解析错误原因:404请求返回的是HTML格式的错误页面,前端强制用response.json()解析非JSON内容,触发语法错误。
  3. 其他潜在问题:
    • 后端用this.otpCode存储验证码,普通函数中this指向错误,无法正确存储/读取OTP。
    • 前端判断data.success字段,但后端返回的是message字段,逻辑不匹配。
    • 需求是4位OTP,后端生成的却是6位。
    • Node.js环境中误用Python的print函数,应替换为console.log。
    • Nodemailer账号密码硬编码,存在安全风险,需改用环境变量。

修复方案

1. 后端代码(server.js)修复

const express = require("express");
const path = require("path");
const nodemailer = require("nodemailer");
const dotenv = require('dotenv');
dotenv.config();

const bodyParser = require('body-parser');

const app = express();
const port = 5000;

// 全局变量存储OTP(单实例临时方案,仅测试用)
let otpCode = null;

app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.get("/", (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'login.html'));
});

// 生成并发送OTP接口
app.post("/generateOTP", (req, res) => {
  const { email } = req.body;

  // 生成4位OTP
  otpCode = Math.floor(1000 + Math.random() * 9000);
  const mailOptions = {
    from: process.env.EMAIL_USER,
    to: email,
    subject: "登录验证码",
    text: `你的登录验证码是:${otpCode}`,
  };

  transporter.sendMail(mailOptions, (error, _info) => {
    if (error) {
      console.error('发送邮件失败: ', error);
      res.status(500).json({ success: false, message: '发送验证码失败' });
    } else {
      console.log('验证码已发送: ', otpCode);
      res.status(200).json({ success: true, message: '验证码已发送至邮箱' });
    }
  });
});

// 新增OTP验证接口,匹配前端请求路径
app.post("/verify-otp", (req, res) => {
  const { email, otp } = req.body;
  // 验证OTP,成功后清空避免重复使用
  if (otp == otpCode && otpCode !== null) {
    console.log('OTP验证通过:', otp);
    otpCode = null;
    res.status(200).json({ success: true, message: '登录成功' });
  } else {
    res.status(401).json({ success: false, message: '验证码无效' });
  }
});

// Nodemailer使用环境变量配置
const transporter = nodemailer.createTransport({
  service: 'gmail',
  auth: {
      user: process.env.EMAIL_USER,
      pass: process.env.EMAIL_APP_PASSWORD // Gmail需用应用专用密码
  }
});

app.listen(port, () => {
  console.log(`服务器运行在 http://localhost:${port}`);
});

2. 前端代码(login.html)脚本修复

const otpBtn = document.getElementById('otp-btn');
const loginBtn = document.getElementById('loginBtn');

loginBtn.disabled = true;

otpBtn.addEventListener('click', async function(event) {
  event.preventDefault();
  const email = document.querySelector('input[name="email"]').value;
  
  try {
    const response = await fetch('/generateOTP', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ email })
    });
    
    // 先检查响应状态,避免非200状态下解析JSON失败
    if (!response.ok) {
      throw new Error(`请求失败: ${response.status}`);
    }
    
    const data = await response.json();
    if (data.success) {
      alert('验证码已发送至你的邮箱!');
      loginBtn.disabled = false;
    } else {
      alert(data.message || '发送验证码失败,请稍后重试。');
    }
  } catch (error) {
    console.error('错误:', error);
    alert('发送验证码时出现错误,请检查服务器状态。');
  }
});

document.querySelector('form').addEventListener('submit', async function(event) {
  event.preventDefault();
  const email = document.querySelector('input[name="email"]').value;
  const otp = document.querySelector('input[name="otp"]').value;
  
  try {
    const response = await fetch('/verify-otp', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ email, otp })
    });
    
    if (!response.ok) {
      throw new Error(`请求失败: ${response.status}`);
    }
    
    const data = await response.json();
    if (data.success) {
      alert('验证码验证成功!');
      window.location.href = '/MF.html';
    } else {
      alert(data.message || '验证码无效,请重新输入。');
    }
  } catch (error) {
    console.error('错误:', error);
    alert('验证验证码时出现错误,请稍后重试。');
  }
});

3. 环境变量配置

在项目根目录创建.env文件,填入以下内容(替换为自己的Gmail账号和应用专用密码):

EMAIL_USER=your-gmail-address@gmail.com
EMAIL_APP_PASSWORD=your-gmail-app-password

注意事项

  • Gmail需开启两步验证,并创建应用专用密码才能正常发送邮件。
  • 全局变量存储OTP仅适用于单服务器实例,生产环境需改用Redis等内存存储方案。
  • 无数据库场景下,无法关联邮箱与OTP,存在不同用户OTP混淆的风险,仅供测试使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 00:32:20