Node.js和Express的会话问题

14

我遇到了会话问题,有时候我刚刚设置的会话变量在下一个页面请求时会变成未定义。我通常需要再次执行流程才能正确设置变量。

我确认我的代码没有将会话变量设置为未定义;它们都具有合法值。

在我的应用程序中,用户从/twitter/connect/移动到/twitter/callback/。前者从Twitter检索一些oauth数据,后者将用户登录到Twitter。

/twitter/connect/很简单:

app.get('/twitter/connect/?', function(req, res){
    consumer().getOAuthRequestToken(function(error, oauthToken, oauthTokenSecret, results){
        if (error){
            // error handling here
        } else {
            req.session.oauthRequestToken = oauthToken;
            req.session.oauthRequestTokenSecret = oauthTokenSecret;

            // if I console.log the two session variables above
            // they have the proper values.

            res.redirect("https://twitter.com/oauth/authorize?oauth_token="+req.session.oauthRequestToken);      
        }
    });
});

之后,Twitter会将它们发送回/twitter/callback/:

app.get('/twitter/callback/?', function(req, res){
    console.log(req.session.oauthRequestToken);
    console.log(req.session.oauthRequestTokenSecret);

    // more often than not, the two variables above are
    // undefined.  but not always.  usually on the first
    // pass, never on the second.
});

我不知道发生了什么事情,我可以确认会话变量被正确设置,但它们在页面请求之间不保留值,仅在第一次出现此问题。

以下是我创建服务器的方式:

app.configure('development', function(){
    app.use(express.cookieParser());
    app.use(express.session({ secret:'yodawgyo' }));
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
    app.use(express.logger());
    app.use(express.static(__dirname + '/public'));
    app.set('view engine', 'ejs');
    app.set('view options', {
        open: '{{',
        close: '}}'
    });
});

目前我只有一个开发环境。我已安装 Node 0.5.0-pre,但在 0.4.1 版本中也遇到了此问题。我正在使用 express 2.3.2。

非常感谢任何帮助。


2
我在调用API时遇到了完全相同的问题。我非常确定会话信息在重定向时丢失。也就是说,如果我在浏览器中刷新页面,我可以看到我放在会话中的东西仍然存在。但是,如果我通过某些外部网站(例如oauth提供者)的重定向到达相同的页面,则会话完全为空白。不确定问题出在哪里。 - Yevgeniy Brikman
更新:看起来重定向请求会获得不同的会话 ID。 - Yevgeniy Brikman
4个回答

19
在Connect的会话中,任何处理程序都可以将req.session.anything设置为任何值,并且当您的处理程序调用end()时,Connect将存储该值。如果同时有多个请求在进行,则这是危险的;当它们完成时,一个会话值将覆盖另一个值。这是具有如此简单的会话API(或直接查看会话源)的后果,它没有支持原子性地获取和设置会话属性。
解决方法是尽可能少地向会话中间件提供请求。以下是一些提示:
  1. 将您的express.static处理程序置于会话中间件之上。
  2. 如果您无法将一些不需要会话的处理程序上移,也可以配置会话中间件以忽略不使用req.session的任何路径,例如express.session.ignore.push('/individual/path')
  3. 如果任何处理程序不写入会话(可能只从会话中读取),则在调用res.end();之前设置req.session = null;。然后它就不会被重新保存。
如果一次仅有一个请求对会话进行读取-修改-写入操作,则覆盖的可能性会更小。我希望在将来,Connect会有一个更精确的会话中间件,但是当然API会比我们现在拥有的更复杂。

1
感谢您详细的回复。不幸的是,这并没有解决我的问题。我将静态处理程序移到了会话中间件之上,但这似乎是我所能做的全部。所有路由(仅有4个)都直接使用会话或通过使用会话的中间件进行传递。还有其他想法吗? - ehynds
感谢您的详细解释。我已经在 GitHub 上开了一个 issue 来跟踪它:https://github.com/senchalabs/connect/issues/854 - Matt
哇,你的第一个解决方案解决了问题。由于某种原因,在POST请求的页面之间我的会话断开了。我不知道为什么,但它确实发生了。一旦我将静态文件移到会话中间件之上,它就可以工作了! - Alon Carmel
什么是静态?我没有这样的东西,当我从路由处理程序设置一个会话变量时,我的数据库中出现了18个重复的会话行。 - FlavorScape

6
我可以帮您进行翻译。以下是您需要翻译的内容:

刚刚遇到了同样的问题,但已经解决了。 简单的答案是调用

 Session.save() 

如果你不能依赖express-session在响应结束时自动调用它,那么需要显式地调用它。

参考文献


4

我曾经对Express在GitHub上提出问题, 但几分钟后就发现了问题所在。不确定你是否遇到了同样的问题,但为了完整起见:

在我的情况下,我是通过访问http://localhost:8003 进行测试的,但OAuth提供程序将重定向到http://hostname:8003。虽然是同一台服务器/网页,但不同的域名意味着浏览器发送不同的cookie,因此express获得不同的会话ID和会话数据。只要我开始在 http://hostname:8003 进行测试,一切都正常了。


2

我的代码在结构上与你的类似。但是,在我的情况下,我的POST操作起源于JQuery $.post函数。我不知道这是否有显着的区别,然而

我最终不得不在我的express路由程序中的POST操作定义末尾发送一个“垃圾”响应,即:

res.send("hoo hee ha ha unimportant junk data");

通过在末尾包含这个,我能够触发一个成功函数(AJAX post尝试的回调)。我在那里插入了重定向,而不是将其放在express路由中。

使用AJAX Post window.redirect();

不要使用AJAX Post:$res.redirect("someurl");

这是因为浏览器显然不会在AJAX响应上重定向,所以必须使用Javascript,如此处所述:Express js - can't redirect

希望这对某人有所帮助,它为我解决了问题,祝好。


我喜欢一点老式的 _呼嘿哈哈_。没有什么比得上它的了。 - Engineer
谢谢你,安瓦尔!那拯救了我一个巨大的头痛。运作得很好。非常奇怪。 - crowmagnumb

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