【问题标题】:Session/context lost in Play async/awaitPlay async/await 中的会话/上下文丢失
【发布时间】:2012-12-31 08:46:01
【问题描述】:

await() 方法似乎失去了上下文:

public static action() {
    session.put("key", "value");
    await(someAsyncCall());

    // Now, for some reason the session doesn't have "key"
}

这是一个已知问题吗?有什么解决方法吗?

【问题讨论】:

    标签: playframework playframework-1.x


    【解决方案1】:

    这很不幸。由于 session 是一个线程局部变量,它不会在新线程之间传递(在您的示例中发生)。令人误解和令人惊讶的是,当代码在 await 方法之后恢复时,会出现一个会话变量(但它是一个不同的实例)。

    我会说这是一个错误 - 我希望在 await 调用周围维护会话上下文。

    也就是说,我明白为什么这很棘手。当您使用 await 时,您实际上是在至少三个线程中编写代码。之前的部分、作业/异步调用和之后的部分。追根溯源,有点神奇。

    即便如此,我同意应该保持请求的会话状态,所以我建议你提出问题:https://play.lighthouseapp.com/projects/57987-play-framework/tickets/new

    以下是通过异步调用传递会话映射来复制会话映射的解决方法。您可以编写一个始终执行此操作的简单包装器 Job。

    public static void test() {
        Logger.debug("before: Session.current() " + Session.current());
        Session.current().put("key", new Date().toString());
        Job<Session> async = new Job<Session>() {
            Session sessionPassed = Session.current();
    
            @Override
            public Session doJobWithResult() throws Exception {
                Logger.debug("during job: Session.current() "
                        + Session.current());
                Logger.debug("during job: sessionPassed " + sessionPassed);
                Thread.sleep(1000L);
    
                // you could do something like this to wrap a real 
                // async call and maintain the session context.  If 
                // the async job returns a result, you'll have to return 
                // a map or POJO with the session and the result.
    
                actualJob.now();  
    
                return sessionPassed;
            }
        };
        Session sessionReturned = await(async.now());
        Logger.debug("after: Session.current() ="
                + (Session.current() == null ? "no session" : Session.current()));
        Logger.debug("after: " + sessionReturned);
    
        Session.current().all().putAll(sessionReturned.all());
    
        Logger.debug("finally: "
                + (Session.current() == null ? "no session" : Session.current()));
    }
    

    编辑:

    或者,您可以使用 Cache.set() 存储会话映射 - 这可能比传递它更干净。

    顺便说一句,我很少使用会话来存储用户数据。每个 cookie(即会话正在运行的内容)都会减慢您的 http 请求(了解 cookie 的工作原理)。我更喜欢做的是使用缓存在服务器端创建一个地图(例如 Cache.set(session.getId(),userDataMap))。显然每个用例可能不同,但我更喜欢这种方式来维护用户状态。

    【讨论】:

    【解决方案2】:

    Play 1.2.5 的解决方法,如果只需要保留会话 ID,请使用以下命令代替直接调用 await(...)

    protected static <T> T awaitSessionAware(Future<T> future) {
        final String sessionId = session.getId();
        T result = await(future);
        session.put("___ID", sessionId);
        return result;
    }
    

    上面的代码是针对here 概述的问题的解决方法,其中在 await(..) 调用之后创建了一个新会话,而不是重用现有会话。对原始会话 ID 的引用用于在等待调用后重置会话 ID(即 session.put("___ID", sessionId) 将会话 ID 重置为其预等待值)。

    【讨论】:

    • 也许稍微解释一下会有所帮助。
    猜你喜欢
    • 1970-01-01
    • 2019-08-05
    • 2015-11-13
    • 2014-06-11
    • 2017-04-05
    • 1970-01-01
    • 2015-02-17
    • 1970-01-01
    • 2011-11-11
    相关资源
    最近更新 更多