Session/context lost in Play async/await

假装没事ソ 提交于 2019-12-08 08:17:50

问题


It seems the await() method loses context:

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

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

Is this a known issue? Any workarounds?


回答1:


That is unfortunate. Since session is a thread local variable it does not get passed between new threads (which happens in your example). What is misleading and surprising, is that when the code resumes after the await method there is a session variable (but it is a different instance).

I would say this is a bug - I'd expect the session context to be maintained around the await call.

That said, I understand why this is tricky. When you use await, you are actually writing code in at least three threads. The before part, the job/async call, and the after part. Trace into it, it is kind of amazing.

Even so, I agree that the session state for the request should be maintained, so I suggest you file an issue: https://play.lighthouseapp.com/projects/57987-play-framework/tickets/new

Below is a workaround that copies the session map by passing it through the async call. You could write a simple wrapper Job that always does this.

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()));
}

EDIT:

Alternatively, you could store the session map using Cache.set() - this is perhaps cleaner than passing it around.

As an aside, I seldom use session to store user data. Each cookie (which is what a session is in play) slows down your http requests (read about how cookies work). What I prefer to do is create a map on the server side using the Cache (eg Cache.set(session.getId(),userDataMap)). Clearly each use case may be different, but I prefer this way to maintain user state.




回答2:


Workaround for Play 1.2.5, if all that is required is to persist the session id, use the following in place of direct call to await(...)

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

The code above is a workaround for the issue outlined here where a new session is created after the await(..) call instead of reusing the existing session. A reference to the original session id is used to reset the session id after the await call (i.e. session.put("___ID", sessionId) resets the session id to its pre-await value).



来源:https://stackoverflow.com/questions/14099463/session-context-lost-in-play-async-await

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!