简体   繁体   English

为什么在这种情况下发送后无法设置 Header?

[英]Why am I getting can't set Header after they are sent in this case?

exports.checkTokenMW = async (req, res, next) => {
  try{  
    const token = req.cookies["access"]   
    const authData = await this.verifyAccessToken(token);
    console.log(authData, "checkingTokenmw")   
     res.json(authData)
  }catch(err){     
    res.status(401);
  }
  next()
};

exports.verifyAccessToken = async (accessToken) => {  
  return new Promise((resolve, reject) => {    
    jwt.verify(accessToken, keys.accessTokenSecret, (err, authData) => {          
      if(err){
        console.log(err)
        return reject(createError.Unauthorized()); 
      }
      console.log(authData, "accesstoken")
     return resolve(authData);   
    });
  })
};

exports.verifyRefreshToken = async (refreshToken, res) => {
  return new Promise((resolve, reject) =>{
    //const refreshToken = req.body.refreshToken;
    
    jwt.verify(refreshToken, keys.refreshTokenSecret, (err, authData) =>{
      if (err) { reject(createError.Unauthorized()); return }      
      const userId = authData.userId
      return resolve(userId)
    })
  })
};

exports.logOut = async (req, res) => {
  try{
      console.log("----------- logout")
      const refreshToken = req.cookies["refresh"];     
      if(!refreshToken) {throw createError.BadRequest();}
      const user =  await this.verifyRefreshToken(refreshToken);     
       res.clearCookie("refresh")
       res.clearCookie("access")

      res.sendStatus(204);
      res.redirect("/");

      return res.send()
  }catch(err){   
    console.log(err)
  }
};

**The routes file has code something like this ** **路由文件有类似这样的代码**

app.get('/api/logout', authService.checkTokenMW,authService.logOut) app.get('/api/logout', authService.checkTokenMW,authService.logOut)

I have been trying to tackle this error from a while not exactly sure what header is setting itself multiple times一段时间以来,我一直在尝试解决此错误,但不确定 header 多次设置自身

**Here is the error ** **这里是错误**

** **

>     Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
>         at ServerResponse.setHeader (_http_outgoing.js:561:11)
>         at ServerResponse.header (E:\COURSE-WEBSITE\main-backend\wiz_backend\node_modules\express\lib\response.js:771:10)
>         at ServerResponse.append (E:\COURSE-WEBSITE\main-backend\wiz_backend\node_modules\express\lib\response.js:732:15)
>         at ServerResponse.res.cookie (E:\COURSE-WEBSITE\main-backend\wiz_backend\node_modules\express\lib\response.js:857:8)
>         at ServerResponse.clearCookie (E:\COURSE-WEBSITE\main-backend\wiz_backend\node_modules\express\lib\response.js:804:15)
>         at exports.logOut (E:\COURSE-WEBSITE\main-backend\wiz_backend\controllers\auth-controller.js:131:13)
>         at processTicksAndRejections (internal/process/task_queues.js:95:5) {
>       code: 'ERR_HTTP_HEADERS_SENT'
>     }

** **

The problem is inside the middleware function.问题出在中间件 function 内部。 Once you check for authData you don't need to send it back to response using res.json(authData) .检查 authData 后,您无需使用res.json(authData)将其发送回响应。 Because after that response will be sent and your next() function will be triggered anyways.因为在发送响应之后,您的next() function 无论如何都会被触发。 Since next() will be called your route handler will try to also send another response which is the conflict you face.由于next()将被调用,您的路由处理程序还将尝试发送另一个响应,这是您面临的冲突。 At the same time, in catch block, you need to have a return statement, so the function execution will be stopped, and it will not reach till next()同时在catch块中需要有return语句,所以function的执行会停止,直到next()

exports.checkTokenMW = async (req, res, next) => {
  try{  
    const token = req.cookies["access"]   
    const authData = await this.verifyAccessToken(token);
    console.log(authData, "checkingTokenmw")   
    // res.json(authData) // <- remove this line
  }catch(err){     
    return res.status(401); // <- here
  }
  next()
};

The specific error you report is caused when your code tries to send more than one response to a given incoming request.当您的代码尝试向给定的传入请求发送多个响应时,会导致您报告的特定错误。 You get to send one and only one response per request.每个请求您只能发送一个响应。 So, all code paths, including error code paths have to be very careful to make sure that you are sending exactly one response.因此,所有代码路径(包括错误代码路径)都必须非常小心,以确保您发送的正是一个响应。

Plus, if you have already sent a response, do not call next() because that will continue routing to other requests and eventually attempt to send some response (a 404 if no other handlers match).另外,如果您已经发送了响应,请不要调用next() ,因为这将继续路由到其他请求并最终尝试发送一些响应(如果没有其他处理程序匹配,则为 404)。

With these in mind, you have several places your code needs fixing.考虑到这些,您的代码有几个地方需要修复。

In your checkTokenWM:在您的 checkTokenWM 中:

exports.checkTokenMW = async (req, res, next) => {
  try{  
    const token = req.cookies["access"]   
    const authData = await this.verifyAccessToken(token);
    console.log(authData, "checkingTokenmw")   
     res.json(authData)
  }catch(err){     
    res.status(401);
  }
  next()
};

You are calling res.json(authData) and then also calling next() .您正在调用res.json(authData) ,然后还调用next() But, if the purpose of this is to be middleware that continues routing, then you should not be sending any response here if the token passes.但是,如果这样做的目的是成为继续路由的中间件,那么如果令牌通过,您不应该在此处发送任何响应。

Then, in the catch block, you're setting a status, but not sending a response - that's not technically wrong, but probably not what you want.然后,在 catch 块中,您正在设置状态,但不发送响应 - 这在技术上并没有错,但可能不是您想要的。 I'd suggest fixing both of those like this:我建议像这样修复这两个:

exports.checkTokenMW = async (req, res, next) => {
  try{  
    const token = req.cookies["access"]   
    const authData = await this.verifyAccessToken(token);
    console.log(authData, "checkingTokenmw");
    // the access token verified so continue routing
    next();
  }catch(err){     
    // token did not verify, send error response
    res.sendStatus(401);
  }
};

In your logout:在您的注销中:

exports.logOut = async (req, res) => {
  try{
      console.log("----------- logout")
      const refreshToken = req.cookies["refresh"];     
      if(!refreshToken) {throw createError.BadRequest();}
      const user =  await this.verifyRefreshToken(refreshToken);     
       res.clearCookie("refresh")
       res.clearCookie("access")

      res.sendStatus(204);
      res.redirect("/");

      return res.send()
  }catch(err){   
    console.log(err)
  }
};

You are attempting to send three responses upon successful logout and no responses upon error.您尝试在成功注销时发送三个响应,而在出错时不响应。

First off, res.sendStatus(204) sets the status AND sends an empty response.首先, res.sendStatus(204)设置状态并发送一个空响应。 res.status(204) would just set the status for a future call that actually sends the response if that's what you meant to do. res.status(204)只会为将来实际发送响应的调用设置状态,如果那是你的意思。 But, with that status, you can't then do res.redirect() .但是,在这种状态下,你不能再做res.redirect()

It's not entirely clear what you're trying to do here.目前尚不清楚您要在这里做什么。 If you want to redirect, then that needs to be a 3xx status so you can't use 204. I'm going to assume you want to do a redirect.如果你想重定向,那么它必须是 3xx 状态,所以你不能使用 204。我假设你想做一个重定向。

I'd suggest fixing by changing to this:我建议通过更改为修复:

exports.logOut = async (req, res) => {
  try{
      console.log("----------- logout")
      const refreshToken = req.cookies["refresh"];     
      if(!refreshToken) {throw createError.BadRequest();}
      const user =  await this.verifyRefreshToken(refreshToken);     
      res.clearCookie("refresh");
      res.clearCookie("access");    
      res.redirect("/");

  }catch(err){   
      // can't call logout, if you weren't logged in
      res.sendStatus(401);
  }
};

FYI, most apps won't make it an error to call logout if you weren't logged in. They would just clear the cookies and redirect to home either way as it's really no issue whether they were previously logged in or not.仅供参考,如果您没有登录,大多数应用程序不会将调用注销作为错误。他们只会清除 cookies 并以任何一种方式重定向到主页,因为无论他们以前是否登录都没有问题。 The problem with doing it your way is if the cookies somehow get corrupted, then your code doesn't let the user attempt to clear things up by logging out and logging back in.这样做的问题是,如果 cookies 以某种方式损坏,那么您的代码不会让用户尝试通过注销并重新登录来清除问题。

So, I'd probably just skip the token check entirely:所以,我可能会完全跳过令牌检查:

exports.logOut = async (req, res) => {
    res.clearCookie("refresh");
    res.clearCookie("access");    
    res.redirect("/");
};

And, also I'd change this:而且,我也会改变这个:

app.get('/api/logout', authService.checkTokenMW,authService.logOut)

to this:对此:

app.get('/api/logout', authService.logOut);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM