5
votes

I'm currently using the session() of play framework in my template :

@if(session().get("email")==null){
    <li><a href="@controllers.routes.General.login">Login</a></li>
}else{
    <li><a href="@controllers.routes.General.logout">Logout</a></li>
}

This template is used in all of my views. Some of these views are controlled by a Java controller, and some are with a Scala controller.

When I click on links that lead to Java controllers, I have no problems, the links for login and logout are correctly handled.

When I click on links that lead to Scala controllers, I get a [RuntimeException: There is no HTTP Context available from here.]

From what I read in here about scala controllers, I understood that they didn't return the http context when rendering a page, but I really want to be able to use the session in my template.

I thought about using an argument session() in my view, templates and controllers, but I believe that there will be a conflict between the java session (play.mvc.http.session) and the scala session (play.api.mvc.session) when play will compile the html pages.

Am I stuck? Is there a possibility to force scala controllers to give back the http context ?

3

3 Answers

2
votes

The root cause maybe the Java controllers and Scala controllers are handled differently. I have my project in Java first, and then try to add more Scala controllers. I also came across this problem (BTW, I am using Play 2.3.2).

I tried to fix this by setting my own Http.Context in the TheadLocal variable using my own ActionBuilder.

import play.api.mvc._
import scala.concurrent.Future
import play.mvc.Http.Context
import play.core.j.JavaHelpers

object ContextAction extends ActionBuilder[Request] {

  def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
    Context.current.set(JavaHelpers.createJavaContext(request))
    block(request)
  }
}

Then my Scala controller actions simply use this ContextAction instead:

class TestController extends Controller {
  def test = ContextAction { implicit request =>
    Ok(views.html.index())
  }
}

And this way the index template can access all request() / session() / etc.

0
votes

I may be wrong, but I think that your Scala controllers should look like:

  def myaction() = Action { implicit request =>
    ...
  }

instead of:

  def myaction() = Action {
    ...
  }

Ie, you have to add the request to the scope of your Action.

And add it also to your view, at the beginning of the file:

@(...)(implicit session:Session)
0
votes

Okay I found a workaround this problem. This is not really aesthetic, but it works, and gets rid of the problem entirely.

I created two different main templates : scalamain.scala.html and javamain.scala.html.

The scalamain template is used by all views that are controlled by a Scala controller, and used the usual trick to use the session (implicit arguments, see more here).

The javamain template is used by all view that are controlled by a Java controller. (these view use the session easily).

The two templates are of course, the same once rendered by play.

I end up with some redundancy in my code and it took to separate all the actions so that view are controlled by only one type of controller(scala or java).

I hope this will help others with the same problem. I validate this answer, as it solves the problem, but feel free to answer if you find a more gracious way to solve it.