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

Node+Express+EJS+Mongoose项目中用户邮箱修改的表单异常排查

问题排查:表单选择器变量名导致fetch提交失效

我开发了基于Node、Express、EJS和Mongoose的简易博客,在实现账户管理的修改邮箱功能时遇到奇怪现象:在account.ejs中,只有把表单选择器从const form = document.querySelector('form')改成const form2 = document.querySelector('form'),才能触发authController里的accountPost方法完成邮箱更新。相关代码如下,求排查原因。


相关代码

account.ejs

<html lang="en">
<%- include("./partials/head.ejs") %>

<body>
  <%- include("./partials/nav.ejs") %>

  <div class="account content">
    <div>
      <h2 class="management-header">Account Management for <%= user.email %></h2>
    </div>
    <div class="alter-email content">
        <p>Change Email</p>
        <hr>
        <form class="alter-email-form" action="/account/<%= user._id %>" method="POST">
          <label for="oldEmail">Old Email</label>
          <input type="text" id="oldEmail" name="oldEmail" required>
          <label for="newEmail">New Email</label>
          <input type="text" id="newEmail" name="newEmail" required>
          <button>Update Email</button>
        </form>
      </div>

  </div>

  <%- include("./partials/footer.ejs") %>

  <script>
    //THIS IS THE PROBLEM
    const form2 = document.querySelector('form');
    form.addEventListener('submit', async (e) => {
        e.preventDefault();
        //get values
        const oldEmail = form.oldEmail.value;
        const newEmail = form.newEmail.value;
        try {
            const res = await fetch('/account', {
                method: 'POST',
                body: JSON.stringify({ oldEmail, newEmail }),
                headers: { 'Content-Type': 'application/json' }
            });
            const data = await res.json();
            console.log(data);
            if(data.user) {
                location.assign('/blogs');
            }
        }
        catch (err) {
            console.log(err);
        }        
    });
  </script>
</body>
</html>

authController.js 中的 accountPost 方法

const accountPost = async (req, res) => {    
    const id = req.params.id;
    const {newEmail, oldEmail} = req.body;
    console.log(newEmail, oldEmail);
    let user = await User.findById(id);
    user.updateOne({
        '_id': id,
        'email': newEmail
    })
    .then(result => {
        res.redirect('/');
    })
}

module.exports = {
    accountPost
}

app.js

const express = require('express');
const morgan = require('morgan');
const mongoose = require('mongoose');
const blogRoutes = require('./routes/blogRoutes');
const authRoutes = require('./routes/authRoutes');
const cookieParser = require('cookie-parser');
const { checkUser } = require('./middleware/authMiddleware');
require('dotenv').config();

//express app
const app = express();

//mongoDB connection string
const dbURI = `mongodb+srv://${process.env.blog_username}:${process.env.blog_password}@nodecourse.h4qkmfb.mongodb.net/nodeCourse?retryWrites=true&w=majority`;

mongoose.connect(dbURI)
    .then((result) => app.listen(3000))
    .catch((err) => console.log(err));

//register view engine
app.set('view engine', 'ejs');

app.get('/', (req, res) => {
    res.redirect('/blogs');
});

app.get('/about', (req, res) => {
    res.render('about', { title: 'About'});
});

app.use(authRoutes);

//404 page
app.use((req, res) => {
    res.status(404).render('404', {title: '404'});
})

问题根源分析

  1. 变量定义与引用不匹配
    看account.ejs的脚本代码:你定义的变量是form2,但事件监听却绑定到了未定义的form变量上。当你用const form = document.querySelector('form')时,页面全局作用域中大概率已经存在同名的form变量(比如nav.ejs里有其他表单,或者之前的脚本定义过form),导致你的局部变量被覆盖,无法正确获取当前页面的修改邮箱表单。改成form2时,变量名无冲突,才能正确绑定事件。

  2. 默认表单提交的干扰
    你的form标签写了action="/account/<%= user._id %>" method="POST",当事件监听绑定失败时,浏览器会触发默认表单提交(直接发POST请求到该action地址),这时候你看到的“更新成功”其实是默认提交触发的,而非你的fetch逻辑。

  3. fetch地址与路由不匹配
    你的fetch请求发往/account,但authController里的accountPost从req.params.id取用户ID,说明路由应为/account/:id,fetch地址写错了。

  4. Mongoose更新逻辑错误
    user.updateOne({...})用法错误,并且缺少旧邮箱验证,存在安全隐患。


修复方案

  1. 修正脚本变量与事件绑定
    用类选择器精准定位表单,避免全局变量冲突:

    const form = document.querySelector('.alter-email-form');
    form.addEventListener('submit', async (e) => {
        e.preventDefault();
        const oldEmail = form.oldEmail.value;
        const newEmail = form.newEmail.value;
        try {
            // 修正fetch地址,带上用户ID
            const res = await fetch('/account/<%= user._id %>', {
                method: 'POST',
                body: JSON.stringify({ oldEmail, newEmail }),
                headers: { 'Content-Type': 'application/json' }
            });
            const data = await res.json();
            console.log(data);
            if(data.success) {
                location.assign('/blogs');
            }
        } catch (err) {
            console.log(err);
        }        
    });
    
  2. 修正后台更新逻辑
    增加旧邮箱验证,用正确的Mongoose更新方式:

    const accountPost = async (req, res) => {    
        const id = req.params.id;
        const { newEmail, oldEmail } = req.body;
        
        // 验证旧邮箱是否正确
        const user = await User.findById(id);
        if (!user || user.email !== oldEmail) {
            return res.json({ success: false, message: '旧邮箱验证失败' });
        }
        
        // 更新并保存邮箱
        user.email = newEmail;
        await user.save();
        
        res.json({ success: true, message: '邮箱更新成功' });
    };
    
  3. 添加JSON解析中间件
    在app.js中添加解析JSON请求体的中间件,否则req.body会为空:

    // 在app.use(authRoutes)之前添加
    app.use(express.json());
    app.use(authRoutes);
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 22:28:18