Express中Node.js的会话未能存储

3

我有以下路由,主要针对登录表单的POST请求进行处理。问题在于当登录成功(找到该用户并且密码匹配)时,成员的_id未存储在会话中。

我的路由:

app.post('/signin', function(req, res) {
    Member.findOne({username: req.body.username}, function(error, member) {
        var matchPassword = crypto.createHmac('sha1', member.salt).update(req.body.password).digest('hex');
        if(member.password == matchPassword) {
            req.session.member_id = member._id;
            res.redirect('/' + member.username);
        }
    });
    res.redirect('/');
});

当触发此路由时,控制台会显示以下错误信息:
node.js:134
        throw e; // process.nextTick error, or 'error' event on first tick
        ^
Error: Can't set headers after they are sent.
    at ServerResponse.<anonymous> (http.js:527:11)
    at ServerResponse.setHeader (/Users/admin/Node Projects/sandboxProject/node_modules/express/node_modules/connect/lib/patch.js:62:20)
    at ServerResponse.header (/Users/admin/Node Projects/sandboxProject/node_modules/express/lib/response.js:280:8)
    at ServerResponse.redirect (/Users/admin/Node Projects/sandboxProject/node_modules/express/lib/response.js:413:10)
    at Promise.<anonymous> (/Users/admin/Node Projects/sandboxProject/app.js:109:8)
    at Promise.<anonymous> (/Users/admin/Node Projects/sandboxProject/node_modules/mongoose/lib/promise.js:120:8)
    at Promise.<anonymous> (events.js:64:17)
    at Promise.emit (/Users/admin/Node Projects/sandboxProject/node_modules/mongoose/lib/promise.js:59:38)
    at Promise.complete (/Users/admin/Node Projects/sandboxProject/node_modules/mongoose/lib/promise.js:70:20)
    at /Users/admin/Node Projects/sandboxProject/node_modules/mongoose/lib/query.js:1087:15

似乎在 "req.session.member_id = member._id;" 之后的 "res.redirect('/' + member.username);" 不起作用。这是因为 Mongoose findOne 回调函数的异步特性吗?我已经尝试从回调函数中删除 res.redirect,但会话数据未被存储,而当我将其保留时,会出现“headers already sent”错误。
我的配置:
app.configure(function(){
    app.set('views', __dirname + '/views');
    app.set('view engine', 'jade');
    app.use(express.cookieParser());
    app.use(express.session({ secret: "blahblah" }));
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(app.router);
    app.use(express.static(__dirname + '/public'));
});

已经解决了问题 - 原来很简单,只需要一点常识!由于Mongoose的回调是异步的,并在先前版本路由中的“res.redirect('/');”之后触发会话存储。您只需要在回调内处理失败并从路由回调结尾删除“res.redirect('/');”即可。糟糕! - Ashley Dawson
如果下面的解决方案有效,能否选择一个作为正确答案,以帮助其他遇到相同问题的人? - Brandon
3个回答

8
我有一个类似的问题。虽然我的问题是,当我从会话中删除某些内容时,例如delete req.session.user,在重新加载和节点应用程序重新启动后,它仍然存在。只有在req.session.destroy()之后才会消失。
我查看了Node的会话中间件文档,发现他们有一个Session#save()方法。因此,在会话对象操作之后,我执行了req.session.save(),并解决了我的问题。请尝试一下,看看是否可以解决你的问题。

尽管这个答案指引了我正确的方向,但我想明确一下 req.session.save() 是一个带有回调函数参数的异步函数。因此,任何依赖于保存数据的操作都应该在回调函数内执行。当仅使用内存存储进行开发并且操作是“实时”的时候,这通常不是很明显。 - Peter Pajchl

4
问题在于Member.findOne是异步的。在其回调函数执行时,res.redirect('/') 已经执行了,并设置了适当的 HTTP 响应头。根据 HTTP 规则,您不能发送两次标头,因此会出现错误Can't set headers after they are sent. 您可能希望将redirect('/')移动到密码匹配的 else 代码块中。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接