I'm working on a Scala Play application and need many Controller actions disabling caching of the browser by setting parameters in the HTTP headers of the Response. I decided to create a NoCache composite action and since I am also using Deadbolt-2 (and need a Deadbolt-2's AuthenticatedRequest[_]) it looks like this:
package action
import be.objectify.deadbolt.scala.AuthenticatedRequest
import play.api.http.HeaderNames
import play.api.mvc._
import scala.concurrent.Future
import scala.util.Success
case class NoCache[A](action: Action[A]) extends Action[A] with HeaderNames {
def apply(request: AuthenticatedRequest[A]): Future[Result] = {
action(request).andThen {
case Success(result) => result.withHeaders(
(CACHE_CONTROL -> "no-cache, no-store, must-revalidate"),
(PRAGMA -> "no-cache"),
(EXPIRES -> "0")
)
}
}
lazy val parser = action.parser
}
but then it won't compile trying to mix in this Action into my Controller action implementations e.g.
def link = deadbolt.SubjectPresent()() andThen NoCache() { implicit request =>
or
def link = NoCache(deadbolt.SubjectPresent()()) { implicit request =>
but can't see how to compose them ...
I found how to do it for a single action:
def index = NoCache {
deadbolt.WithAuthRequest()() { implicit request =>
Future {
Ok(views.html.index(userService))
}
}
}
However, I still haven't found how to apply NoCache to the entire controller class.
Related
I'm trying to make all my rest api endpoints return the token with which the user used to make the request (already exists in all request).
This is how my basic endpoint looks like:
def getAll(meta: MetaData): Action[AnyContent] = deadbolt.Restrict(role)(parse.default) {
implicit request => myService.getAll(meta).map(result => {
val results: Seq[Resp] = result._2.map(res => Mapper(res))
Ok(Json.toJson(MyListResponse.apply(meta, result._1, results)))
})
}
How can I add to this my response the token's information received from in the request?
Thanks!
As you described in comment section you need to get token from query parameter and add it response header. Let's suppose that this token is optional, hence might be absent, so this can be achieved something like:
implicit request => myService.getAll(meta).map { result =>
// Fetch optional token as query parameter. `token_parameter` - parameter key
val requestToken = request.queryString.get("token_parameter").flatMap(_.headOption)
val results: Seq[Resp] = result._2.map(res => Mapper(res))
val body = Json.toJson(MyListResponse.apply(meta, result._1, results))
// Add token to header if it is present
requestToken.fold(Ok(body))(token => Ok(body).withHeaders("token_header" -> token))
}
Update
In order to apply this logic to ALL the routes, you can use Play Filters feature. Please, see doc for more details: https://www.playframework.com/documentation/latest/ScalaHttpFilters
What you need to do:
1) Implement your own Play Filter. It would look something like next:
import javax.inject.Inject
import akka.stream.Materializer
import play.api.mvc._
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
class TokenFilter #Inject() (implicit val mat: Materializer, ec: ExecutionContext) extends Filter {
def apply(nextFilter: RequestHeader => Future[Result])
(requestHeader: RequestHeader): Future[Result] = {
nextFilter(requestHeader).map { result =>
val requestToken = requestHeader.queryString.get("token_parameter").flatMap(_.headOption)
requestToken.fold(result )(token => result.withHeaders("token_header" -> token))
}
}
}
2) Wire filter to rest of application. For instance, via adding next config in application.conf
play.http.filters += com.yourcomany.TokenFilter
Hope this helps!
Before Play 2.6 I had some custom actions e.g. NoCache action where I only had to care about implementing the apply method i.e.
package actions
import play.api.http.HeaderNames
import play.api.mvc._
import scala.concurrent.Future
/**
* Custom Action composition implementation that disables client-side browser caching
* by changing the response header of the response adding multi-browser no-cache
* parameters. The composition can be done as follows:
* {{{
*
* def link = NoCache {
* deadbolt.SubjectPresent()() { implicit request =>
* Future {
* Ok(views.html.account.link(userService, auth))
* }
* }
* }
*
* }}}
*
* #param action The inner action
* #tparam A Action type
*/
case class NoCache[A](action: Action[A]) extends Action[A] with HeaderNames {
def apply(request: Request[A]): Future[Result] = {
action(request).map { result =>
result.withHeaders(
(CACHE_CONTROL -> "no-cache, no-store, must-revalidate"),
(PRAGMA -> "no-cache"),
(EXPIRES -> "0")
)
}
}
}
Now in Play 2.6 I get a bunch of errors because Action now needs overriding executionContext and parser. I see nothing in this 2.6 but further complexity but anyways ... I manage to override the former using global but I don't see a way to provide a simple implementation for the later.
How can I specify an I don't care BodyParser for my custom Action?
import scala.concurrent.ExecutionContext.Implicits.global
override def executionContext = global
override val parser: BodyParser[A] = null // <<<<<< what else here?
There is a section on the migration guide about it:
The Scala ActionBuilder trait has been modified to specify the type of the body as a type parameter, and add an abstract parser member as the default body parsers. You will need to modify your ActionBuilders and pass the body parser directly.
It could be better worded, but anyway, the way you "pass the body parser" is using dependency injection. For example, from actions composition docs:
import play.api.mvc._
class LoggingAction #Inject() (parser: BodyParsers.Default)(implicit ec: ExecutionContext) extends ActionBuilderImpl(parser) {
override def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
Logger.info("Calling action")
block(request)
}
}
And later you inject your action in your controllers:
class MyController #Inject()(
loggingAction: LoggingAction,
cc: ControllerComponents
) extends AbstractController(cc) {
def index = loggingAction {
Ok("Hello World")
}
}
Of course, you can use the same approach in your NoCache action:
import play.api.mvc._
import scala.concurrent._
import play.api.http.HeaderNames._
class NoCache #Inject() (parser: BodyParsers.Default)(implicit ec: ExecutionContext) extends ActionBuilderImpl(parser) {
override def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
block(request).map { res => res.withHeaders(
(CACHE_CONTROL -> "no-cache, no-store, must-revalidate"),
(PRAGMA -> "no-cache"),
(EXPIRES -> "0")
)}
}
}
Also, as you can see, we are not using the global execution context, but instead the one available through dependency injection. This is the default execution context for the application, so it is easier to configure if you need to.
Looking at the implementations of BodyParser you could give the value of BodyParser.Empty
override val parser: BodyParser[A] = BodyParser.Empty
https://www.playframework.com/documentation/2.6.7/api/java/play/mvc/BodyParser.html
I'm trying to integrate Deadbolt2 into my play framework 2.4 application.
A have following piece of code in my controller
import be.objectify.deadbolt.scala.{ActionBuilders, AuthenticatedRequest, DeadboltActions}
import be.objectify.deadbolt.scala.cache.HandlerCache
import play.api.data.Form
import play.api.data.Forms._
import play.api.mvc.{AnyContent, _}
import modules.user.security.{Authenticator, HandlerKeys, MyDeadboltHandler}
class Login #Inject() (deadbolt: DeadboltActions, handlers: HandlerCache, actionBuilder: ActionBuilders) extends Controller {
//...
def login = deadbolt.SubjectNotPresent() {
Action { implicit request =>
Ok(login(loginForm))
}
}
}
And I got error missing parameter type. I'm following deadbolt examples, which helps me a lot, but I can't figure out how to pass implicit request into action.
My template begins like this:
#(loginForm: Form[LoginForm])(implicit flash: Flash)
Using Action directly without deadbolt.SubjectNotPresent() works well.
One persistent question that keeps coming up is "how do I get the subject in an authorized action?". As of Deadbolt 2.5, the request passed into an action has been replaced with an AuthenticatedRequest which contains an Option[Subject]. As users of 2.4 also want this feature, it has been included in 2.4.4 as a breaking change.
The following examples use SubjectPresent as an example, but the same change applies to all authorization constraints.
When using action builders, in place of
def index = actionBuilder.SubjectPresentAction().defaultHandler() { implicit request
Ok(accessOk())
}
we now have
def index = actionBuilder.SubjectPresentAction().defaultHandler() { authRequest =>
Future {
Ok(accessOk())
}
}
When using action composition, in place of
def index = deadbolt.SubjectPresent() { implicit request
Action {
Ok(accessOk())
}
}
we now have
def someFunctionA = deadbolt.SubjectPresent()() { authRequest =>
Future {
Ok("Content accessible")
}
}
The getSubject() function of the DeadboltHandler trait now takes an AuthenticatedRequest instead of a Request.
override def getSubject[A](request: AuthenticatedRequest[A]): Future[Option[Subject]] =
request.subject match {
case Some(user) => Future {request.subject}
case None => // get from database, identity platform, cache, whatever
}
What this means for your app is
def login = deadbolt.SubjectNotPresent() {
Action { implicit request =>
Ok(login(loginForm))
}
}
becomes
def login = deadbolt.SubjectNotPresent()() { authRequest =>
Future {
Ok(login(loginForm))
}
}
Trying to better understand creating custom actions using action builder.
I have a custom action like:
class LogginInRequest[A](val currentUser: User, request: Request[A]) extends WrappedRequest[A](request)
object ActionWithContext extends ActionBuilder[LogginInRequest] {
def invokeBlock[A](request: Request[A], block: (LogginInRequest[A]) => Future[SimpleResult]) = {
val u = /// load User
block(new LogginInRequest(user, request)
}
}
Could I someone create another custom action like the above that inherits from the above one and builds upon it i.e. chain in the action and access the user object
e.g. Say I now create a new section on my website for admins, so I check the user object in this new action for:
user.isAdmin
If isAdmin is false, then I redirect using the Future.successful(Forbidden) call.
So I would create LoggedInAdmin based on LogginInRequest but simply checking for the property isAdmin (this is just an example but I want to know if I can do this).
Is it possible to pass an argument to my custom action that I created using action builder so I can do this?
def someAction(...):= MyCustomActionBuilder(userService) {
// ...
}
i.e. I am passing in a argument to it.
With Play 2.3, action composition using ActionBuilder got a whole lot better. The example below shows how you can define a standard ActionBuilder that creates a wrapped request, and then chain that builder together with an ActionTransformer to create a new wrapped request.
import scala.concurrent.Future
import org.scalatest.{MustMatchers, WordSpec}
import play.api.mvc._
import play.api.test.FakeRequest
import play.api.test.Helpers._
class ChainingTest extends WordSpec with MustMatchers {
class WrappedReq1[A](val str: String, request: Request[A]) extends WrappedRequest[A](request)
object ActionWithReq1 extends ActionBuilder[WrappedReq1] {
def invokeBlock[A](request: Request[A], block: (WrappedReq1[A]) => Future[Result]) = {
block(new WrappedReq1(request.headers("1"), request))
}
}
class WrappedReq2[A](val str1: String, val str2: String, request: Request[A]) extends WrappedRequest[A](request)
val ActionWithReq2 = ActionWithReq1 andThen new ActionTransformer[WrappedReq1, WrappedReq2] {
override protected def transform[A](request: WrappedReq1[A]): Future[WrappedReq2[A]] = {
Future.successful(new WrappedReq2[A](request.str, request.headers("2"), request))
}
}
"chained actions" should {
"work" in {
val request = FakeRequest().withHeaders("1" -> "one", "2" -> "two")
val response = ActionWithReq2 { r => Results.Ok(r.str1 + "-" + r.str2) }(request)
contentAsString(response) must equal("one-two")
}
}
}
There is an ActionTransformer trait for modifying your request wrapper, and an ActionFilter for intercepting certain requests and immediately returning a result.
ScalaActionsComposition describes the traits in more detail.
I'm aware from Setting HTTP headers in Play 2.0 (scala)? that you can set response headers on a case-by-case basis by doing, for example, Ok("hello").withHeaders(PRAGMA -> "no-cache").
What if you want to set that header, or a few different headers, on responses from all your Actions? You wouldn't want to repeat the withHeaders everywhere. And since this is more like an application-wide configuration, you might not want Action writers to have to use a different syntax to get your headers (e.g. OkWithHeaders(...))
What I have now is a base Controller class that looks like
class ContextController extends Controller {
...
def Ok(h: Html) = Results.Ok(h).withHeaders(PRAGMA -> "no-cache")
}
but that doesn't feel quite right. It feels like there should be more of an AOP-style way of defining the default headers and having them added to each response.
The topic is quite old now, but with Play 2.1 it is even simpler now.
Your Global.scala class should look like this :
import play.api._
import play.api.mvc._
import play.api.http.HeaderNames._
/**
* Global application settings.
*/
object Global extends GlobalSettings {
/**
* Global action composition.
*/
override def doFilter(action: EssentialAction): EssentialAction = EssentialAction { request =>
action.apply(request).map(_.withHeaders(
PRAGMA -> "no-cache"
))
}
}
In your Global.scala, wrap every call in an action:
import play.api._
import play.api.mvc._
import play.api.Play.current
import play.api.http.HeaderNames._
object Global extends GlobalSettings {
def NoCache[A](action: Action[A]): Action[A] = Action(action.parser) { request =>
action(request) match {
case s: SimpleResult[_] => s.withHeaders(PRAGMA -> "no-cache")
case result => result
}
}
override def onRouteRequest(request: RequestHeader): Option[Handler] = {
if (Play.isDev) {
super.onRouteRequest(request).map {
case action: Action[_] => NoCache(action)
case other => other
}
} else {
super.onRouteRequest(request)
}
}
}
In this case, I only call the action in dev mode, which makes most sense for a no-cache instruction.
The easiest way to achieve fine-grained control is in using wrapped actions. In your case it can be something like that:
object HeaderWriter {
def apply(f: Request[AnyContent] => SimpleResult):Action[AnyContent] = {
Action { request =>
f(request).withHeaders(PRAGMA -> "no-cache")
}
}
}
and use it in such manner:
def doAction = HeaderWriter { request =>
... do any stuff your want ...
Ok("Thats it!")
}
There are too ways. You can use action-composition. Then you must declare at every Controller that you want set here the header. Another option is to use the GlobalSettings.
There are similar solutions for java, too.