[2.0-java] Get routes parameters

1,995 views
Skip to first unread message

Leone

unread,
Aug 30, 2012, 3:26:39 PM8/30/12
to play-fr...@googlegroups.com
Hi!

We are developing an application with Play Framework 2.0 and we have a route like this:

GET    /account/:id/action/:param     controllers.AccountController.action(id:Int, param:String)

Is there a way to get the :id parameter with Play API? We want to verify the URL before executing actions in some controllers.

Thank you!

James Roper

unread,
Aug 30, 2012, 5:58:19 PM8/30/12
to play-fr...@googlegroups.com
You mean when doing action composition?  Unfortunately no :(  But I'd be very interested in adding this feature at some point in future, it's great for doing authorisation on REST resources.  The current work around is to parse the incoming URL yourself.

Leone

unread,
Aug 30, 2012, 6:16:18 PM8/30/12
to play-fr...@googlegroups.com
Yes. I'm using it in action composition. We are doing everything in session and some times it is not necessary. :(

Julien Richard-Foy

unread,
Aug 31, 2012, 3:18:57 AM8/31/12
to play-fr...@googlegroups.com
The effort is not so high: instead of writing:

def foo(id, param) = MyAction { … }

you currently need to write:

def foo(id, param) = MyAction(id) { … }
> --
> You received this message because you are subscribed to the Google Groups
> "play-framework" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/play-framework/-/tHTKCdWmkgkJ.
>
> To post to this group, send email to play-fr...@googlegroups.com.
> To unsubscribe from this group, send email to
> play-framewor...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/play-framework?hl=en.

James Roper

unread,
Aug 31, 2012, 3:28:26 AM8/31/12
to play-fr...@googlegroups.com
That's fine for Scala, but doesn't work in Java.

Jxtps

unread,
Aug 31, 2012, 5:51:19 PM8/31/12
to play-fr...@googlegroups.com
I've been looking for this as well. Logged #622 a month ago:
 
 
Any love from the core team on this one?
 
 
@JRF: Yes, it's possible to create a custom action that takes the additional info, but that creates a lot of duplicate boiler plate, the avoidance of which was a major reason for going Scala in the first place.

Julien Richard-Foy

unread,
Sep 1, 2012, 8:26:50 AM9/1/12
to play-fr...@googlegroups.com
By design Play actions are isomorphic to HTTP requests parameters (it
allows to perform both routing and reverse routing in a type safe
way), so anyway your actions would be required to take path parameters
as parameters:

GET /foo/:bar controllers.Application.foo()

The above route definition can’t work because the reverse routing
process wouldn’t allow to set the value of the `bar` parameter. So you
need to define your route as follows:

GET /foo/:bar controllers.Application.foo(bar)

public static Result foo(String bar) {
return ok;
}

So inside actions you can directly get the `bar` parameter value. Can
you show a case where you need to get a path parameter outside of an
action body?

Julien

Jxtps

unread,
Sep 1, 2012, 11:33:40 AM9/1/12
to play-fr...@googlegroups.com
Absolutely, it's useful for REST-style routing with nested resources. 

So right now I have a project where Users can have many Websites. Each Website in turn has a ton of model objects associated with it (Events, Posts, etc). So I get routes like: 

GET /websites/:websiteId/events controllers.websites.Events.list(websiteId, page:Int ?= 0)

The handler for this action then looks like: 

object Events extends WebsitesControllerBase {
  def list( websiteId : String, page: Int) = WebsiteAdminAction(websiteId)(website => implicit request => {
    new ListPage(website, page, Event.paginateEx(page, where = Seq("websiteId" -> website.id.get)))
  })
}

Where as you pointed out in your earlier post, the websiteId needs to be passed into a custom Action (marked in red) that thereby has access to it - otherwise it can't fetch & authorize access to the website. 

Note that there is now a categorical difference between the "page" parameter and the "websiteId" parameter since one is available through the queryString and the other is not, which leads to this non-uniform access. Yes, the workaround works (it is in fact what I'm using now), but it's indicative that something is not ideal. 

I suggest two things: 

1.a Add a parameters:Map[String, Seq[String]] to RequestHeader that gets *all* the parameters associated with the request (much like FormUtils does for Form.bindFromRequest). As a consumer of the framework I really don't care where the parameter came from - Do What I Mean (DWIM), not what I said. 

1.b Yes, the HTTP spec certainly allows multiple entries for the request parameters, but it is *very* rarely used in practice. So a better signature might be: 
parameters:Map[String, String] and parametersWithMultipleEntries:Map[String, Seq[String]] where the latter is only parsed if asked for (and it also penalizes use like .asInstanceOf[OtherClass] does).


2. For the routing, could one do a reverse route with two parameter lists? I.e. 

GET   /foo/:bar    controllers.Application.foo(gazonk)  

would result in a reverse route like so: 

controllers.routes.Application.foo(gazonk)(bar)

That probably breaks a lot of internal assumptions, but it would work nicely from a user-of-framework perspective.




Jxtps

unread,
Sep 1, 2012, 11:37:25 AM9/1/12
to play-fr...@googlegroups.com
Note that with these suggestions implemented, the code would reduce to: 

GET /websites/:websiteId/events controllers.websites.Events.list(page:Int ?= 0)

and 

object Events extends WebsitesControllerBase {
  def list( page: Int) = WebsiteAdminAction(website => implicit request => {
    new ListPage(website, page, Event.paginateEx(page, where = Seq("websiteId" -> website.id.get)))
  })
}

which would be very nice - the websiteId is really pure boilerplate... 

Julien Richard-Foy

unread,
Sep 1, 2012, 1:27:30 PM9/1/12
to play-fr...@googlegroups.com
> 1.a Add a parameters:Map[String, Seq[String]] to RequestHeader that gets
> *all* the parameters associated with the request (much like FormUtils does
> for Form.bindFromRequest). As a consumer of the framework I really don't
> care where the parameter came from - Do What I Mean (DWIM), not what I said.
>
> 1.b Yes, the HTTP spec certainly allows multiple entries for the request
> parameters, but it is *very* rarely used in practice. So a better signature
> might be:
> parameters:Map[String, String] and parametersWithMultipleEntries:Map[String,
> Seq[String]] where the latter is only parsed if asked for (and it also
> penalizes use like .asInstanceOf[OtherClass] does).

I don’t think it make sense to give a common interface for *all*
request parameters, maybe we could just add a `pathParams: Map[String,
String]` field to the RequestHeader interface. This field would only
contain parameters extracted from the request path.

> 2. For the routing, could one do a reverse route with two parameter lists?
> I.e.
>
> GET /foo/:bar controllers.Application.foo(gazonk)
>
> would result in a reverse route like so:
>
> controllers.routes.Application.foo(gazonk)(bar)
>
> That probably breaks a lot of internal assumptions, but it would work nicely
> from a user-of-framework perspective.

Yes, finding a way to avoid the requirement of passing every path
parameter to the action would be interesting. The solution you
suggested is interesting but doesn’t play well with Java: users would
still need to write `controllers.routes.Application.foo(gazonk, bar)`
since there is no multiple parameters lists support in Java.

Leone

unread,
Sep 4, 2012, 9:16:42 AM9/4/12
to play-fr...@googlegroups.com
Hi Julien,

I think your pathParams idea is the best solution to the problem. This helps a lot when is necessary to create more generic Actions as Authorization Systems. However, we redeveloped our system entirely in Scala using Play Scala and Squeryl. Even Play! Framework tries to target both languages (Scala and Java) we have much more liberty and possibilites using Scala. After transition we figured out that we writed 2x less code and it was much more concise than the Java version.

Jxtps

unread,
Sep 5, 2012, 4:27:11 PM9/5/12
to play-fr...@googlegroups.com
1: Common interface: Ah, but it does!
 
As a user of the framework, why should I care where the parameter came from? It is just that: a parameter. Nothing more, nothing less. If it's in the path, if it's in the query string, if it's in the body - my controller doesn't care. It just needs to know what the value for the named parameter is.
 
It makes it easier to change things - and great flexibility is one of Play's virtues, n'est-ce pas?
 
(The separated out piece probably also makes sense, and makes it easy to copy-paste FormUtils someplace where I can access it and voila, I can create a helper function that brings together all parameters, but it makes me sad to think that not all other play developers would have immediate access to it as well...)
 
 
2: Why require isomorphism? While intellectually neat, is it useful? Yes, it does assist in documenting the interface, but why enforce it? The path parameter must by necessity be provided when creating the URL, but not when calling the action - isn't that a strength?

Julien Richard-Foy

unread,
Sep 5, 2012, 4:36:44 PM9/5/12
to play-fr...@googlegroups.com
> 1: Common interface: Ah, but it does!
>
> As a user of the framework, why should I care where the parameter came from?

Because the shape of the possible query parameters varies according to
its origin: a path parameter can only have one value, a query string
parameter can have multiple values, and a body parameter can have
nested values. Providing a single API to retrieve them would to either
a limited API or a too complex API. That’s actually because you’re
trying to factor out different things.

> 2: Why require isomorphism?

Because it allows to do reverse routing in a type safe and intuitive way.

Jxtps

unread,
Sep 6, 2012, 9:04:20 AM9/6/12
to play-fr...@googlegroups.com
Since the parameter types are defined in the routes file, we could do:

Routes:
GET /websites/:websiteId/events controllers.websites.Events.list(page:Int ?= 0)(websiteId:Int)

Scala could then use identical syntax in the reverse route, while java would have the extra params tacked on to the end as you described.

This is type safe, as intuitive as you're going to get, and fully opt in - anyone who doesn't want to use/learn it doesn't have to since you can still put everything in the first parameter list.

(sorry for beating on a dead horse, but the briefer, clearer, cleaner syntax I laid out above isn't possible without this)

Pierre Lepropre

unread,
Nov 5, 2013, 5:37:26 AM11/5/13
to play-fr...@googlegroups.com
Hello guys,

Sorry if I seem to dig out this quite old thread. That's the only interesting piece I've found so far discussing my current issue with play.
I'm finding myself in such a situation where I have to handle a - potentially - high number of repeated query parameters such as 

GET /main?param1=foo&param1=bar&param1=foobar

Apparently, this is still not possible to handle it directly with the routing (say, with Julien's proposal for example) and the only way I've found so far is to
get the query paramers from the request object while writing my Action. Unless I'm mistaken, that involves writing some boilerplate code.

Am I anywhere wrong in this ?

Btw, I think that any up-to-date solution to this issue should be worth mentionning in the documentation, after the handling of optional parameters for example.
I'll gladly send a pull request for this is suited.

Pierre.
Reply all
Reply to author
Forward
0 new messages