【问题标题】:Express authentication - cannot send headers after sent快速身份验证 - 发送后无法发送标头
【发布时间】:2012-01-07 18:58:06
【问题描述】:

我创建了一个函数,通过验证存储的会话用户/通行证与数据库中的内容匹配来限制路由访问

var checkAuth = function(req, res, next){
  if(typeof(req.session.user) === 'undefined') {
    req.session.user = { name: '', pass: '', loggedIn: false }
  }
  $R.user.validateLogin(req.session.user, function(err){
    if(err) res.redirect('/login')
    else {
      req.session.user.loggedIn = true
      next()
    }
  })
}

app.get('/restricted', checkAuth, function(req, response){
  response.render('index')
})

它似乎工作正常,因为如果一个人没有被认证,它会重定向到 /login 页面,但在重定向后应用程序立即关闭并出现错误

错误:发送后无法设置标头。

我已将错误追溯到 res.redirect('/login') 但不知道如何纠正我的错误。

编辑:我的登录路由处理程序

app.get('/login', function(req, response){
  $R.page.addStyles(['forms','user/user'])
  response.render('user/login')
})
app.post('/login', function(req, response){
  $R.user.validateLogin(req.body, function(err, res){
    if(err) response.end(JSON.stringify({error: err.message}))
    else {
      req.session.user = req.body
      response.end(JSON.stringify({ok: true}))
    }
  })
})

【问题讨论】:

  • 我认为错误不在您显示的代码中,而是在路由“/login”的处理程序中
  • 似乎在 $R.page.addStyles 您正在为响应写一些东西。当您尝试 response.render('user/login') 时,这将导致错误。如果你在 .render() 之前使用 res.write()、.end()、.send() 或 .json(),这就是你要找的。​​span>
  • 我只在从 checkAuth() 重定向后才收到错误 addStyles 仅将文件路径添加到模块数组以包含在布局中,并且如果我在 url 中输入 /login 则工作正常。错误是根据堆栈输出的 res.redirect(/login)

标签: node.js express


【解决方案1】:

你的问题是函数:

$R.user.validateLogin(req.session.user, function(err){

是异步的。 checkAuth 函数应立即返回真/假,或重定向。你当前的登录流程是这样的:

  1. app.get('/restricted') 触发
  2. checkAuth 触发
  3. $R.user.validateLogin 触发(异步
  4. 此时,checkAuth 将控制权返回给 app.get('/restricted')
  5. response.render('index') 执行
  6. $R.user.validateLogin 内的代码执行,调用重定向。

问题是您无法控制是先执行 5 还是 6。最终,两者都会执行,因为您没有阻止 #5 的发生。

要解决此问题,您的 checkAuth 函数需要在不使用内部回调(或同步执行回调)的情况下返回和/或重定向。由于您已经在“登录”路由中验证用户登录,因此您应该能够检查用户会话并同步返回或执行重定向,如下所示:

var checkAuth = function(req, res, next){
  if(typeof(req.session.user) === 'undefined') {
    req.session.user = { name: '', pass: '', loggedIn: false }
  }

  if (!req.session.user.loggedIn) {
    // req.session.user.loggedIn = true should be set in the 'login' route, in $R.user.validateLogin
    res.redirect('/login');
  } else {
    // if we already have a req.session.user and they are logged in, keep going
    next();
  }
}

对于任何语法错误,我没有测试上面的代码。

【讨论】:

  • 我假设因为 next() 回调的存在表明路由中间件也是异步的,只有在调用 next() 时才会继续。你的解决方案奏效了,谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-21
  • 2012-10-22
相关资源
最近更新 更多