[Play 2.7 Java] Issues with deprecated Http.Context

I’m trying to migrate an old Play application to Play 2.7 starting by zero and I encounter some issues.

  1. In my main template I used something like that to enable some menus items:
    @if(session.get(“role”) == “SUPERADMIN” || session.get(“role”) == “ADMIN”)
    now session.get is deprecated.
    Which is the new syntax?

  2. Authentication. Before 2.7 I used something like that:

   public class Secured extends Security.Authenticator {
    @Override
    public String getUsername(Http.Context ctx) {
        return ctx.session().get("id");
    }

    @Override
    public Result onUnauthorized(Http.Context ctx) {
        ctx.flash().put("danger", "You need to login before access the application.");
        return redirect(controllers.routes.HomeController.login());
    }
}

and in the controller I used:

 @Security.Authenticated(Secured.class)
    public  Result index() {
        return ok(index.render());
    }

Now everything seems to be deprecated. But if I use only @Security.Authenticated() I’m unable to enter in the method cause:
Unauthorized
You must be authenticated to access this page.

To authenticate the user I use the following method:

public Result authenticate(Http.Request request) {
        DynamicForm requestData = formFactory.form().bindFromRequest(request);
        String email = requestData.get("email").trim();
        String password = requestData.get("password").trim();
        User user = userRepository.findByEmail(email);

        if (user == null) {
            return redirect(routes.HomeController.login()).flashing("danger", "Incorrect email or password.");
        }
        if(!checkPassword(password, user.getPassword())) {
            return redirect(routes.HomeController.login()).flashing("danger", "Incorrect email or password.");
        }
     return redirect(routes.HomeController.index()).addingToSession(request, "role", user.getRole()).
                addingToSession(request,"email", user.getEmail()).
                addingToSession(request, "id", user.getId());

    }

Have I to set something in my authentication method?

Thanks

I can answer your first question.
You need to explicitly pass in an instance of play.mvc.Http.Request into your template, and use request.session().getOptional("role").orElse(null).

Thanks, but in this case since it is the main template, have I to pass Request on every template?

If you need to access request data, then the answer is yes.

If there isn’t other solution, don’t seems an enhancement if in all my templates I’ve to pass the Request object that I use only in the main template.

I agree. Reminds me of the change in 2.6 where you need to explicitly pass in the HttpExecutionContext for certain cases. While I understand why, but it feels more and more cumbersome and sometimes cause runtime errors if missed.

It’s very tedious to pass Http.Request to all templates and to all actions. Moreover I’ve to add it to each routes action.

1 Like

For the second question, I think to had solved as follow:

public class Secured extends Security.Authenticator {

    @Override
    public Optional getUsername(Http.Request ctx) {
        return ctx.session().getOptional("id");
    }
    @Override
    public Result onUnauthorized(Http.Request ctx) {
        return redirect(controllers.routes.HomeController.login()).flashing("danger",  "You need to login before access the application.");
   
    }
}

First of all, thanks for your feedback here.

The funny thing is that HttpExecutionContext only exists because of Http.Context which uses a thread-local and then needs tricks when you are moving the execution from one thread to another one. As soon as Http .Context disappears, I’m pretty sure HttpExecutionContext can also disappear as well. In the end what we want is to move away from a thread-local that you need to worry about, gives the wrong expectations (can Http.Context.current() be accessed from anywhere? Why not?), is hard to test, to debug and to reason about when it fails.

I understand that passing the request to your view may be verbose and we will try to find ways to reduce it. Suggestions from users are very welcomed, by the way. How would you make the session data available without passing it as a parameter and without the thread-local problems?

Best.

Yes, that is the way to go. I notice that we don’t have an example about this in our migration guide:

Do you want to submit a pull request to document it? I think it will be pretty useful for other users.

Best.

I totally agree with the removal of Http.Context.current(), for all the reasons you and the team have listed, i.e. promotes (indirectly as a side effect) bad design patterns and usages, etc…

I am excited to hear that HttpExecutionContext can also go away. Is this already happening or a WIP for future enhancements?

I use Play mostly as backend API services, so having to pass request objects around in templates does not affect me as much, and the few templates I do have, I am already passing in the request object due to some weird i18n issues, but I can see it being annoy for others.

It is not happening yet. We have a stale PR now to remove most of the things related to Http.Context. But since we didn’t deprecate HttpExecutionContext in 2.7, we will first do it in the next major release, and later removed. For now, that are no concrete plans yet. So stay tuned. :slight_smile:

Can you elaborate on these problems with i18n?

Best.

Yes of course. How can I do that?

Another issue. In 2.6 I created a little template to use in all the other templates (as @wflash()) for display flash message based on a severity, like the following:

@import helper._

@flash.map {
    case (key, value) => {
        <div class="alert alert-@key">
            @value                     
       </div>
    }
}

So, using it with something like
flash(“danger”, “something is wrong”)

I see a red box with the message inside. Is there a way to convert it to use with 2.7?

For the moment I solved with the following ugly code:

@(implicit request: Http.Request)

@import java.math.BigInteger; var alert="danger";
    
@if(!request.flash().data().isEmpty) {
    @if(request.flash().data().get("success")) {
        @{alert = "success"}
    } 
    <div class="alert alert-@alert">
    @for(v <- request.flash().data().values()) {
        @v
    }
    </div>
}

Thanks! The file you need to edit is this one:

You can submit a PR to add specific information about the Authenticator changes. As you can see there, we tried to have very complete examples of what the code was before and after the migration.

request.flash().asScala().map may help here.

Best.

It doesn’t works:
value map is not a member of play.api.mvc.Flash

I submitted the PR.

request.flash().asScala().map

It doesn’t works:
value map is not a member of play.api.mvc.Flash

No solution for that?