Reputation: 6182
With a route like:
GET /users/:id controllers.Users.view(id)
In the controller, I can read a custom backTo
query string param like this:
def view (id: String) = Action {r =>
val backTo = r.getQueryString("backTo") // read custom param
...
}
Assuming backTo
is an optional query string parameter that I don't want to include in the routes definition (maybe all the actions can read it).
How do I construct a URL using reverse-routing that includes the backTo
parameter?
I would expect something like:
routes.Users.view(id).withQueryString("backTo" -> Seq("previous"))
But that doesn't exist.
Upvotes: 2
Views: 1830
Reputation: 6182
I ended up just pimping a withQueryString
method onto Call
that just parses the URL and rebuilds the URL with the extra parameters:
implicit class CallOps (c: Call) {
import org.jboss.netty.handler.codec.http.{QueryStringDecoder, QueryStringEncoder}
import scala.collection.JavaConverters._
def withQueryString(query: (String,Seq[String])*): Call = {
val decoded = new QueryStringDecoder(c.url)
val newUrl = new QueryStringEncoder(decoded.getPath)
val params = decoded.getParameters.asScala.mapValues(_.asScala.toSeq).toSeq
for {
(key, values) <- params ++ query
value <- values
} newUrl.addParam(key, value)
Call(c.method, newUrl.toString, c.fragment)
}
}
And now I can use it like this:
routes.Users.view(id).withQueryString("backTo" -> Seq("previous")).url
I wish I didn't have to re-parse the URL, but by the time I have a Call
, the URL has already been constructed.
Upvotes: 2
Reputation: 55798
What's wrong about using Parameters with default values (without resolving it manually by yourself)? sample:
def view(id: String, backTo: String, forwardTo: String, whatever: String) = Action {
Ok(
"ID: " + id
+ ", backTo: " + backTo
+ ", forwardTo: " + forwardTo
+ ", whatever: " + whatever
)
}
route:
GET /users/:id controllers.Users.view(id, backTo ?= null, forwardTo ?= null, whatever ?= null)
it allows you to make reverse routes with only ID as a required param:
// /users/john-doe
routes.Users.view("john-doe")
by ordered params:
// /users/john-doe?backTo=prev&forwardTo=next&whatever=bar
routes.Users.view("john-doe", "prev", "next", "bar")
or only with named optional params:
// /users/john-doe?whatever=baz
routes.Users.view("john-doe", whatever = "baz")
What's more important it's type-safe as it's absolutely under the Play's routing syntax, without any manual manipulation. Also you don't need to care if you should start your params with ?
or &
char: "?backTo=home"
vs. "&backTo=home"
...
Upvotes: 1
Reputation: 3748
I think you would have to come down to String
level as there is really no such method available (yet).
What the reverse router is giving you is actually a play.api.mvc.Call
object. You can take a look at the source here:
You will see that you can get the absoluteURL()
- which returns you a String
- from here you would have to manipulate it by adding your query parameter:
val url = routes.Users.view(id).absoluteURL() + "?backTo=home"
Upvotes: 1