Node.js无数据库Web应用OTP验证接口404及JSON解析错误排查
无数据库OTP登录Web应用的404与JSON解析错误修复
问题诊断
- 404错误原因:前端发起OTP验证请求的路径是
/verify-otp,但后端未定义该接口,仅通过/login处理验证,路径不匹配导致请求失败。 - JSON解析错误原因:404请求返回的是HTML格式的错误页面,前端强制用
response.json()解析非JSON内容,触发语法错误。 - 其他潜在问题:
- 后端用
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
相关产品推荐
相关产品推荐

