【问题标题】:NodeJs: Cannot set headers after they are sent to the clientNodeJs:将标头发送到客户端后无法设置标头
【发布时间】:2021-03-25 23:41:27
【问题描述】:

我正在使用验证 jwt 令牌在路由中创建一个简单的有效登录检查

在路线中:

router.post("/contacts", async function (req, res, next) {
  await checkLogin(req, res, next);
  res.send(userController.getContacts());
});

async function checkLogin(req, res, next) {
  const loggedIn = await userController.isLoggedIn(req);
  if (!loggedIn) {
    res.status(401).json({ error: "You are not authorized" });
  }
}

在控制器中

async function isLoggedIn(req) {
  let token = req.header("authorization");
  if (!token) {
    return false;
  }
  token = token.substring(7); //remove Bearer from the beginning
  return jwt.verify(token, tokenSecret, function (err, decoded) {
    if (typeof decoded == "object" && decoded.email == req.body.email) {
      return true;
    } else {
      return false;
    }
  });
}

使用有效和无效的令牌都可以正常工作,但使用无效的令牌我得到了这个日志

UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

如果我删除 status(401),则不会显示此错误日志。为什么会发生这种情况以及如何在不触发此错误日志的情况下将 HTTP STATUS 设置为 401?

【问题讨论】:

    标签: node.js express jwt


    【解决方案1】:

    您正在调用checkLogin,它会设置状态代码并回复请求。 checkLogin 完成后,响应已经发送,您无法发送响应。

    您缺少的是一些条件逻辑,如果用户未登录,它将中止处理。通常,这是在 middlewares 中实现的 - 中间件可以在请求到达之前拦截请求处理程序。

    我可以看到您的 checkLogin 实现看起来有点像中间件函数:它接受 next 参数。你可以把它变成这样:

    async function checkLogin(req, res, next) {
      const loggedIn = await userController.isLoggedIn(req);
      if (!loggedIn) {
        res.status(401).json({ error: "You are not authorized" });
        return;
      }
      next();
    }
    router.post("/contacts", checkLogin, function (req, res, next) {
      res.send(userController.getContacts());
    });
    
    

    这告诉 express.js 首先评估 checkLogin 中间件,并且仅在中间件调用 next() 时将控制权传递给处理程序。如果它从不调用 next - 控制永远不会通过,那么经过身份验证的用户的“主要逻辑”将永远不会运行。

    在此处阅读有关中间件的更多信息:http://expressjs.com/en/api.html#middleware-callback-function-examples

    【讨论】:

    • 请注意,我必须编辑答案,因为它在 checkLogin 的条件块中缺少“返回”。不要错过!
    【解决方案2】:

    如果用户未在checkLogin 中登录,您将发送 401,但此方法在您的router.post 中调用,因此它将在路由处理程序中运行下一条语句,即res.send();。但是由于您已经发送了 401 状态,节点在这里抱怨它已经回复了客户端,您正在尝试发送另一个回复。

    您需要在路由处理程序中同时拥有res

    router.post("/contacts", async function (req, res, next) {
      let loggedIn = checkLogin(req, res, next);
      if (!loggedIn) {
        res.status(401).json({ error: "You are not authorized" });
      } else {
        res.send(userController.getContacts());
      }
    });
    
    async function checkLogin(req, res, next) {
        return await userController.isLoggedIn(req);
    }
    

    这应该可以修复错误。

    【讨论】:

      【解决方案3】:

      如果用户未登录,应用程序尝试发送 2 个响应,这就是错误出现的原因。

      第一个回复:

      async function checkLogin(req, res, next) {
        const loggedIn = await userController.isLoggedIn(req);
        if (!loggedIn) {
          res.status(401).json({ error: "You are not authorized" });
        }
      }
      

      当 loggedIn 为 false 时 res.status().json() 将向用户发送响应。

      第二个响应 现在代码返回到调用上述函数的行,因为没有返回语句来停止/跳过执行

      router.post("/contacts", async function (req, res, next) {
        await checkLogin(req, res, next);
        res.send(userController.getContacts());
      });
      

      所以在await checkLogin(req, res, next);res.send(userController.getContacts()); 之后 被调用,这意味着对于一个调用,您的代码正在尝试发送 2 个响应。

      【讨论】:

        猜你喜欢
        • 2021-04-07
        • 1970-01-01
        • 1970-01-01
        • 2021-03-05
        • 1970-01-01
        • 2022-01-11
        • 2021-06-18
        • 2021-12-12
        • 2021-01-01
        相关资源
        最近更新 更多