I am intercepting all the requests to my play application by overriding onRouteRequest method of GlobalSettings. Now, I need to send some data to the despatched action from here so that I dont perform all those calculations in all the actions. How do I set an attribute to the request (play.api.mvc.RequestHeader) object that I pass to the super onRouteRequest method ?
For your need I don't thing using the onRouteRequest
will work (elegantly at least).
But let's try to use a dedicated structure to intercept.
Here is how you could intercept the request, compute some generic stuff and pass it to Action
First of all, here is an Interceptor
object that has a method intercept
and a convenient method username
:
object Interceptor {
def intercept[A, B](f: RequestHeader => Option[B], e: RequestHeader => Result)(action: B => Action[A]): Action[(Action[A], A)] = {
val bodyParser = BodyParser {
request =>
f(request) map {
b =>
val innerAction = action(b)
innerAction.parser(request).mapDone {
body => body.right.map(innerBody => (innerAction, innerBody))
}
} getOrElse {
Done(Left(e(request)), Input.Empty)
}
}
Action(bodyParser) {
request =>
val (innerAction, innerBody) = request.body
innerAction(request.map(_ => innerBody))
}
}
def username[A](check: RequestHeader => Option[String]): ((String) => Action[A]) => Action[(Action[A], A)] = intercept(check, r => Results.Unauthorized("not logged in"))
}
As you can see, the worker function intercept
gives you the opportunity to compute some stuff based on the request content. Which computation result of type B
might failed (Option
), in that case a handler is there to tell what to do.
Having define what to compute, you can define your action
using a function that takes a B
and gives an Action[A]
.
The username
method is just a simple predefined interceptor that can enables us to define how to retrieve the logged in username, just to illustrate.
Now here is how we can use both of them in your Controller
//index is defined for both GET and POST in routes, but fails on POST
// thanks to the interceptor that checks at first the used method
// the case mustn't be handled in the Action definition
def index = Interceptor.intercept(
/*check the method*/
request => if (request.method == "GET") Some(request.method) else None,
/*not a GET => bad request*/
request => BadRequest(request.method + " not allowed")
) { /*the computation result*/method => Action {
Ok("The method : " + method)
}
}
//this controller retrieve the username in the session and renders it in a OK response
def secured = Interceptor.username(r => r.session.get("username")) { username => Action {
Ok("You're logged in as " + username)
}
}
//this enables you to logged in => store in session
def login(u:String) = Action { request => {
Ok("Logged in as " + u) withSession(("username" -> u))
}
}
Now if you have a generic computation you can create your preconfigured interceptor (here I'm using a case class but simply defining a function that partially applies the interceptor
is enough)
case class Intercept[B] (f: RequestHeader => Option[B], e: RequestHeader => Result) {
def apply[A](action: B => Action[A]) = Interceptor.intercept[A,B](f, e)(action)
}
val getInterceptor = Intercept[String](
request => if (request.method == "GET") Some(request.method) else None,
request => BadRequest(request.method + " not allowed")
)
def index2 = getInterceptor { method => Action {
Ok("Da method : " + method)
}
}
EDIT related to the comment:
Accordingly to your comment, here is how you could do using an interceptor (note that I've mocked up the host retrieval and checking)
Using hosted
and anotherHosted
, you'll be able to test this workflow:
Here is the code
def getHost(request:RequestHeader) = request.queryString.get("host").get.head
def checkHost(host:String, b: Boolean) = b
val checkHosted = (b: Boolean) => Intercept[String](
request => {
val host = getHost(request)
Cache.getAs[String](host) match {
case x@Some(_) => x
case None => if (checkHost(host, b)) {
Cache.set(host, host)
Some(host)
} else None
}
},
request => NotFound(getHost(request) + "not hosted")
)
def hosted(b:String) = checkHosted(b.toBoolean) {
host => Action {
Ok("this host is ok : " + host)
}
}
def anotherHosted(b:String) = checkHosted(b.toBoolean) {
host => Action {
Ok("this host is ok : " + host)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With