Reputation: 9621
I have a play 2 app where I use securesocial module. In request there is Identity available but I need my user object (which extends Identity) to pass to my templates (because user contains some additional fields) so for each controller I have code like:
def index = SecuredAction { implicit request =>
implicit val currentUser = UsersDAO.Users.findByIdentityId(request.user.identityId).get
Ok(views.html.platform.support.index())
}
Basically for each controller's method where a user is logged in I am doing a request to the DB to get current logged in user which I found pretty ugly. Is there any way I can have this loaded once per user session?
UPDATE:
I added a partial function to make things a little bit simpler
def loadCurrentUser(f: (User) => Result)(implicit request: SecuredRequest[play.api.mvc.AnyContent]): Result =
(for {
loggedInUser <- UsersDAO.Users.findByIdentityId(request.user.identityId)
} yield f(loggedInUser)).getOrElse(
Redirect(securesocial.controllers.routes.LoginPage.login))
and now each method from controllers will be like:
def index = SecuredAction { implicit request =>
loadCurrentUser { implicit user =>
Ok(views.html.platform.support.index())
}
}
but I would still like to have user in request or pass it trought request somehow after he has logged in and not load it each time from DB.
Upvotes: 0
Views: 126
Reputation: 55569
I don't think it would be wise to store the entire user within the session, as I imagine it's considered bad practice, and it would make updating the session messy when the user object has been changed (particularly by another user, perhaps an administrator).
I would use the cache API if you want to avoid DB calls for every request, however that will still require care for updating/invalidating the cache when the user has changed. At least in the cache it will all be in one place, though.
While your loadCurrentUser
function will make it absolutely sure that user exists in the database, saving the usage of .get
, I think it's a bit much. If loadCurrentUser
was called in the first place, that user should exist, and if they somehow don't, their session should have been invalidated therefore blocking the request at the SecureSocial level.
Consider defining:
implicit def currentUser(implicit request: SecuredRequest[AnyContent]): User = UsersDAO.Users.findByIdentityId(request.user.identityId).get
This would shorten your calls to currentUser
within the SecuredAction
. This is also the technique used by Play2 Auth.
And if you want to avoid a trip to the database with each request, perhaps a function that returns a cached user:
import play.api.cache.Cache
def findByIdentityId(id: Id): Option[User] = ...
def findCachedByIdentityId(id: Id): Option[User] = {
Cache.getAs[User](s"User.$id").orElse{
val user: Option[User] = findByIdentityId(id)
user.foreach(user => Cache.set(s"User.${user.id}"), user)
user
}
}
It would also be worthwhile to look into Action Composition.
Upvotes: 1