MEAN Stack - 发送给客户端后无法设置 headers

MEAN Stack - Cannot set headers after they are sent to client

这让我发疯了.. 我正在处理的应用程序抛出 "Cannot set headers after they are sent to the client" 错误。 我对在 back-end 上写任何东西都很陌生,无法弄清楚为什么会抛出这个问题。 我已经通读了所有关于这个问题的相关帖子,但仍然看不出问题出在哪里。 请帮我找到我丢失的东西。

这是我要访问的路由,userRoutes.js:

router.post('/login', (req, res, next) => {
  console.log('1');
  let fetchedUser;
  HaulerUser.findOne({ username: req.body.username })
    .then(user => {
      console.log('2');
      if (!user) {
        return res.status(401).json({
          message: 'Auth failed'
        });
      }
      console.log('3');
      fetchedUser = user;
      return bcrypt.compare(req.body.password, user.password);
    })
    .then(result => {
      console.log('4');
      if(!result) {
        return res.status(401).json({
          message: 'Auth failed'
        });
      }
      const token = jwt.sign(
        { username: fetchedUser.username, userId: fetchedUser._id },
        'secret_this_should_be_longer',
        { expiresIn: '1hr' }
      );
      console.log('5');
      res.status(200).json({
        token: token,
        expiresIn: 3600,
        userId: fetchedUser._id
      });
      console.log('6');
    })
    .catch(err => {
      console.log('7');
      return res.status(401).json({
        message: 'Invalid authentication credentials',
        error: err
      });
    });
});

此外,它只向控制台打印 1、2、4 和 7????

这是主要的 NodeJS 代码。

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader(
    'Access-Control-Allow-Headers',
    'Origin, X-Requested-With, Content-Type, Accept, Authorization');
  res.setHeader(
    'Access-Control-Allow-Methods',
    'GET, POST, PATCH, PUT, DELETE, OPTIONS'
  );
  next();
});

app.use('/api/user', userRoutes);
app.use('/api/manifests', manifestsRoutes);
app.use('/api/sites', sitesRoutes);

我对这个有点不知所措......我确定它有点小......请帮忙......

如评论所述,您不止一次调用 res.json(),这就是错误的意思。

您 运行 遇到了人们在学习如何链接 Promises 时遇到的一个常见问题。返回 res.status(401).json({...}) 不会拒绝 Promise,而是 returns res.status(401).json({...}) 的结果到下一个 .then() 块,当找不到用户时,该块又在堆栈中执行相同的操作.

您有几个选项可以解决此问题,但最常见的方法是使用异常退出 Promise 链。而不是每个 if 块中的 res.status(401).json({...}),而是 throw new Error('{an error message}')。这将退出链并跳转到您的 .catch(),它发送然后调用 res.status(401).json({...})。然后,您还可以在将通用消息返回给调用者的同时记录特定的错误消息。

router.post('/login', (req, res, next) => {
  console.log('1');
  let fetchedUser;
  HaulerUser.findOne({ username: req.body.username })
    .then(user => {
      console.log('2');
      if (!user) {
        throw new Error(`User '${req.body.username}' not found`);
      }
      console.log('3');
      fetchedUser = user;
      return bcrypt.compare(req.body.password, user.password);
    })
    .then(result => {
      console.log('4');
      if(!result) {
        throw new Error(`Password did not match`);
      }
      const token = jwt.sign(
        { username: fetchedUser.username, userId: fetchedUser._id },
        'secret_this_should_be_longer',
        { expiresIn: '1hr' }
      );
      console.log('5');
      res.status(200).json({
        token: token,
        expiresIn: 3600,
        userId: fetchedUser._id
      });
      console.log('6');
    })
    .catch(err => {
      console.log('7');
      console.log(err);
      res.status(401).json({ // You don't need a return here
        message: 'Invalid authentication credentials',
        error: err
      });
    });
});

如果您使用的是 Node 8 或更高版本,您可以使用 async/await 而不是 .then()/.catch(),您的代码将按预期运行。