Our Play application uses Slick as a data access layer. This means that in almost every action in our app we need to have both the current Request and Slick's database Session implicitly available on the scope.
I want to write a custom Action to do this so we only need one line to do this. What I want to do is write it in such a way that I can write something like this:
def someAction = DBAction { implicit request, session =>
// body with implicit request and session on scope
But this is not valid syntax. Using a tuple does not work because it cannot implicitly unpack the tuple in action body. Is it possible to create a custom action that passes multiple implicit arguments to the body? How do I do this?
You cannot implicit 2 variables like that.
But your Custom action can return a WrappedRequest (an object which encapsulates the request and your session), and you can define an implicit conversion between this wrapped request and your Session.
Try this example, derived from this class.
sealed case class DBRequest(session: Session, private val request: Request[AnyContent])
extends WrappedRequest(request)
trait DBController extends Controller {
// Implicit conversion from DBRequest to Session
implicit def session(implicit request: DBRequest) = request.session
def DBAction(action: DBRequest => Result) = Action { request =>
database.withSession { session =>
action(DBRequest(session, request))
}
}
}
object MyController extends Controller with DBController {
def myAction = DBAction { implicit request =>
println(session)
}
}
#senia was right, currying needs to be used to do this. This is actually addressed in the Play docs but it's a recent addition I missed.
For future reference for people looking for this, my custom action now looks like this:
def DBAction(action: Request[AnyContent] => Session => Result) = {
Action { request =>
database.withSession { session =>
action(request)(session)
}
}
}
Which I can then use in the following way:
def someAction = DBAction { implicit request => implicit session => /* body */ }
Related
I'm developing a scala Web application with play framework, the first time i used filter in order to validate the request but i had no result then i tried to use action builder i had to override 2 methods here is my ActionBuilder Object
object MyJsonAction extends ActionBuilder[Request, Response] with Results {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
println (request.body)// returning null everytime why ?
block(request)
}
override def parser = { // what does this method do, i think the problem started from here :/ ?
null;
}
override protected def executionContext = {
null;
}
}
Well, there are no much resource with play framework just simple examples and there is no example briefly explain what those methods are doing, they tell that you can get request body without telling you how ! everything is ambiguous ! im stuck with it since 3 days a go, and i have no idea what should i do now. thanks
An ActionBuilder is an object that build an Action. An Action combines a BodyParser[T], which reads the request body and constructs a T, with a function Request[T] => Future[Result] to process the T.
The default ActionBuilder does this in a very simple way. It chains together the BodyParser's result and feeds it to the function. If you make a custom ActionBuilder then you can override this process, perhaps by injecting your own logic before or after the function call.
If you're making your own ActionBuilder I'd suggest starting with something like the following as a template:
#Singleton
class MyJsonAction #Inject() (
bodyParsers: PlayBodyParsers,
ec: ExecutionContext) extends ActionBuilder[Request, JsValue] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
println(request.body) // DO SOMETHING HERE
block(request)
}
override def parser = bodyParsers.json
override protected def executionContext = ec
}
Then you can use it in your controllers like this:
class MyController #Inject() (myJsonAction: MyJsonAction) {
def index = myJsonAction { req: Request[JsValue] => ??? }
}
The important piece is the parser: BodyParser[A] it takes the incoming bytes and convert them into a type A.
For instance BodyParser[JsValue] will produce a body of type JsValue. It is used to read Json.
If you are trying to validate an object, maybe an action builder is not the right place to start. You may focus more on the BodyParsers and the json validation
I've built a microservice using Scala and Play and now I need to create a new version of the service that returns the same data as the previous version of the service but in a different JSON format. The service currently uses implicit Writes converters to do this. My controller looks something like this, where MyJsonWrites contains the implicit definitions.
class MyController extends Controller with MyJsonWrites {
def myAction(query: String) = Action.async {
getData(query).map {
results =>
Ok(Json.toJson(results))
}
}
}
trait MyJsonWrites {
implicit val writes1: Writes[SomeDataType]
implicit val writes2: Writes[SomeOtherDataType]
...
}
Now I need a new version of myAction where the JSON is formatted differently. The first attempt I made was to make MyController a base class and have subclasses extend it with their own trait that has the implicit values. Something like this.
class MyNewContoller extends MyController with MyNewJsonWrites
This doesn't work though because the implicit values defined on MyNewJsonWrites are not available in the methods of the super class.
It would be ideal if I could just create a new action on the controller that somehow used the converters defined in MyNewJsonWrites. Sure, I could change the trait to an object and import the implicit values in each method but then I'd have to duplicate the method body of myAction so that the implicits are in scope when I call Json.toJson. I don't want to pass them as implicit parameters to a base method because there are too many of them. I guess I could pass a method as a parameter to the base method that actually does the imports and Json.toJson call. Something like this. I just thought maybe there'd be a better way.
def myBaseAction(query: String, toJson: Seq[MyResultType] => JsValue) = Action.async {
getData(query).map {
results =>
Ok(Json.toJson(results))
}
}
def myActionV1(query: String) = {
def toJson(results: Seq[MyResultType]) = {
import MyJsonWritesV2._
Json.toJson(results)
}
myBaseAction(query, toJson)
}
Instead of relying on scala implicit resolution, you can call your writes directly:
def myBaseAction(query: String, writes: Writes[MyResultType]) = Action.async {
getData(query).map { results =>
val seqWrites: Writes[Seq[MyResultType]] = Writes.seq(writes)
Ok(seqWrites.writes(results))
}
}
def myActionV1(query: String) = myBaseAction(query, MyJsonWritesV1)
def myActionV2(query: String) = myBaseAction(query, MyJsonWritesV2)
Combining an ActionBuilder that transforms a request into a custom WrappedRequest with an additional type parameter, and then combining that with an ActionFilter causes the type of the custom WrappedRequest to be dropped.
Why is this and is there a fix?
For example, lets say I need to an authentication ActionBuilder and an optional, authorisation ActionFilter where the user type we need can vary depending on use.
See this contrived code:
case class AuthRequest[A, U](val request: Request[A], val user: U) extends WrappedRequest[A](request)
case class TestUser(id: String)
case class AuthenticationAction[A]() extends ActionBuilder[({type λ[B] = AuthRequest[B, TestUser]})#λ] {
override def invokeBlock[A](request: Request[A], block: (AuthRequest[A, TestUser]) => Future[Result]): Future[Result] =
block(AuthRequest(request, TestUser("test-id")))
}
case class AuthorisedAction[A]() extends ActionFilter[({type λ[B] = AuthRequest[B, TestUser]})#λ] {
// does some additional authorisation checks for added security
def authorised(user: TestUser) = true
override def filter[A](request: AuthRequest[A, TestUser]) = Future.successful {
if( authorised(request.user) ) None
else Some(Unauthorized)
}
}
Please note: The type function in the above is required as per this answer because of the additional type parameter on the AuthRequest. Again this is required because this API will be used with more than one type of user.
If I then implement a controller that uses the above:
class ExampleController extends Controller {
def secured = (AuthenticationAction() andThen AuthorisedAction()) { request =>
Ok(request.user.id)
}
}
I get a compiler error (value user is not a member of play.api.mvc.WrappedRequest[play.api.mvc.AnyContent]). In other words, the type of the variable request above is WrappedRequest instead of the expected type of AuthRequest[play.api.mvc.AnyContent, TestUser]!
I understand that most use cases the AuthenticationAction and AuthorisedAction would be combined into a single action but, because authorisation is optional, I would like to keep these as separate concerns.
Is this possible? Is this a bug in the Play Framework API? Or a Scala bug? Or human error?
Using your example I was able to compose the actions like this:
class ExampleController extends Controller {
def secured = AuthorisedAction().compose(AuthenticationAction()) { request =>
Ok(request.user.id)
}
}
It's interesting to note that in both cases IntelliJ's type inspection sees request as being of type AuthRequest[AnyContent, TestUser] -- it's only scalac that sees it as a WrappedRequest.
I have a view that takes in an implicit Lang, needed for Play's Messages object. The signature of this view is:
#()(implicit lang: Lang)
Then I have a controller that needs to create the view:
def createView = Action { request =>
Ok(views.html.showView())
}
And then I have an implicit method that I want called to create the Lang that the view needs:
implicit def getLangFromRequest(request: RequestHeader): Lang = {
request.cookies.get("lang") match {
case Some(cookie) => Lang(cookie.value)
case None => Lang("en") // Default
}
}
In other words, the method is supposed to get the language from the cookie if it can or use a default otherwise (I've trimmed out error handling for this question).
However, this implicit conversion isn't taking place. I know that it's able to implicitly convert, because by creating an implicit variable in the controller works:
def createView = Action { request =>
implicit val lang: Lang = request
Ok(views.html.showView())
}
But I'd rather not have to add this boiler plate line everywhere. Presumably I must explicitly call the method because there's some global Lang object that is being used before an implicit conversion.
What are my options here to avoid having to repeat this implicit val lang: Lang = request in every controller?
According to Play documentation
If you have an implicit Request in the scope, it will provide an
implicit Lang value corresponding to the preferred language extracted
from the Accept-Language header and matching one of the application
supported languages.
So you need to change your action to
def createView = Action { implicit request =>
Ok(views.html.showView())
}
and it should work.
Having to call implicit val lang: Lang = request for each controller seems like a lot of work but when you think about it, not really, you already call the Action method (which accepts as a parameter a Request => Result function) whenever you create a new action.
Write a trait that would do all the heavy logic of finding the language, mix it into all controllers and instead of using the Action method, use a method that you write in that trait, that takes in a Request => Lang => Result function, does the logic for the languages and then applies the function parameter to the request in scope. Something like this:
trait Internationalization { subj: Controller =>
def withLanguage(f: => Request[AnyContent] => Lang => Result) = Action { implicit request =>
val lang = request.cookies.get("lang") match {
case Some(cookie) => Lang(cookie.value)
case None => Lang("en") // Default
}
f(request)(lang)
}
}
Then, instead of def action = Action { ... }, just mix in the trait and use:
def action = withLanguage { request => implicit lang =>
Ok(views.html.showView())
}
the function consume an action, return the same action, what's the purpose of it, it has neither side effect nor any transformation. Looks to me it doesn't nothing, like var a=a,how is it useful?
protected def composeAction[A](action: Action[A]): Action[A] = action
By itself, it doesn't do anything, as you say. It's meant as a placeholder to be overridden when composing Actions, as described in the Play 2.2 release highlights :
We now provide an ActionBuilder trait for Scala applications that
allows more powerful building of action stacks. For example:
object MyAction extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
// Authenticate the action and wrap the request in an authenticated request
getUserFromRequest(request).map { user =>
block(new AuthenticatedRequest(user, request))
} getOrElse Future.successful(Forbidden)
}
// Compose the action with a logging action, a CSRF checking action, and an action that only allows HTTPS
def composeAction[A](action: Action[A]) =
LoggingAction(CheckCSRF(OnlyHttpsAction(action)))
}