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

Node.js+Express+AngularJS登录应用响应配置及重复键错误排查

解决Node.js + Express + MongoDB登录注册中的响应头与重复键错误

我来帮你分析并解决这两个问题,它们其实都是因为异步逻辑处理不当和索引约束导致的:

错误原因解析

1. Error: Can't set headers after they are sent.

你的代码犯了典型的异步逻辑错误:findOne和insert是两个独立的异步操作,会并行执行——不管findOne有没有找到用户,insert都会运行,而且两个回调里都调用了res.end()这类发送响应的方法。这就导致同一个HTTP请求被多次发送响应,Express自然会报错“不能在响应发送后再设置头部”。

2. MongoError: E11000 duplicate key error collection

这个错误是因为你的users集合对username或email字段设置了唯一索引,当你尝试插入已存在的用户名/邮箱时,MongoDB会拒绝操作并抛出重复键错误。而你之前的逻辑没有先判断用户是否存在就直接执行插入,所以触发了这个问题。

修正后的后端代码

我们需要把逻辑改成串行执行:先检查用户是否存在,根据结果决定返回“用户已存在”,还是执行插入操作。同时要规范错误处理,避免直接throw err导致服务器崩溃:

app.post('/signin', function (req, res) {
  console.log('Received POST request to /signin');
  console.log(req.body);

  // 第一步:检查用户是否已存在
  db.users.findOne(
    { $or: [{ username: req.body.username }, { email: req.body.email }] },
    function (err, existingUser) {
      if (err) {
        // 数据库查询错误,返回500状态码和错误信息
        return res.status(500).send({ error: 'Database query failed', details: err });
      }

      if (existingUser) {
        // 用户已存在,返回400(请求错误)状态码和提示
        return res.status(400).send({ message: 'User already exists in database' });
      }

      // 第二步:用户不存在,执行插入操作
      db.users.insert(
        {
          username: req.body.username,
          firstname: req.body.firstname,
          lastname: req.body.lastname,
          email: req.body.email,
          password: req.body.password, // 注意:生产环境一定要用bcrypt等工具加密密码!
          createdAt: new Date(),
          role: "user",
          approvedBy: req.body.approver?.username || null, // 处理approver可能不存在的情况
          status: "locked"
        },
        function (err, insertedData) {
          if (err) {
            // 插入错误,返回500状态码
            return res.status(500).send({ error: 'Failed to create user', details: err });
          }
          // 用户创建成功,返回201(创建成功)状态码和数据
          res.status(201).send({ message: 'User created successfully', data: insertedData });
        }
      );
    }
  );
});

重要提示:

  • 密码加密:生产环境绝对不能明文存储密码,推荐使用bcrypt库对密码进行哈希处理后再存入数据库。
  • 状态码规范:使用合适的HTTP状态码(比如400表示客户端请求错误,500表示服务器内部错误,201表示资源创建成功),让前端能更清晰地判断请求结果。
  • 错误处理:不要在Express回调里直接throw err,这会导致服务器崩溃,应该用res.status().send()把错误返回给客户端。

前端代码优化建议

你的前端代码基本没问题,如果你需要显式设置请求头,建议使用标准的JSON格式:

$http({
  url: '/signin',
  method: "POST",
  data: $scope.SignInForm,
  headers: { 'Content-Type': 'application/json' } // AngularJS默认会设置这个,也可以省略
})
.then(function (res) {
  console.log('Success:', res.data);
}, function (error) {
  console.log('Error:', error.data); // 打印服务器返回的错误详情
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:00:17