-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Alter Router.Routes to give PathPattern information #3378
Comments
This is already provided, through the |
I'm not sure what you say is possible, perhaps I have not stated my requirement clearly enough? I wanted to handle the request at the earliest opportunity (GlobalSettings.onRequestReceived) but the tags do not exist in the request until GlobalSettings.doFilter - but putting this aside, how is it possible to take the following information ROUTE_PATTERN = /x/$name<[^/]+>/$age<[^/]+> and deduce there is a name:String = "Pete" and an age: Int = 41 from the current requestHeader? |
val p = """\$([^<]+)<([^>]+)>""".r
override def onRequestReceived(request: RequestHeader) = {
val (taggedRequest, handler) = super.onRequestReceived(request)
val pattern = taggedRequest.tags("ROUTE_PATTERN")
val paramNames = p.findAllMatchIn(pattern).map(m => m.group(1)).toList
val pathRegex = ("^" + p.replaceAllIn(pattern, m => "(" + m.group(2) + ")") + "$").r
val paramValues = pathRegex.findFirstMatchIn(request.path).get.subgroups
val params: Map[String, String] = paramNames.zip(paramValues).toMap
// ^ your params map, will be Map("name" -> "Pete", "age" -> "41")
(taggedRequest, handler)
} |
That said, there are usually better, more typesafe ways to achieve whatever you're trying to achieve. If you depend on there being specific parameters in the URL, then a filter is not the right thing, because filters apply to all requests, whether they have those parameters or not. Rather, you should probably be using action composition or a custom action builder, like so: case class MyAction(name: String, age: Int) extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
// Do your filtering here, you have access to both the name and age above
block(request)
}
}
def foo(name: String, age: Int) = MyAction(name, age) { request =>
Ok("Hello world")
}
def bar(name: String, age: Int) = MyAction(name, age).async { request =>
Future.successful(Ok("Hello world"))
} |
Many thanks! |
One more question though, how would I also determine the parameter types? |
While possible, I don't think it's worth it. Do you need to know the parameter types? Or is it fine to just convert them as needed? |
It's not imperative, but knowing the types of the parameters would allow for much more control. One of the things I like about C# asp.mvc is that you can subclass a route and use your own for definitions, allowing you to add more information to the route itself (e.g. a coder-specified unique ID) rather than having to decorate actions on controllers, but as the routes file is a kind of DSL it seems this kind of thing wouldn't be possible, would it? |
It's still quite hacky to look at a The thing about Play's routes is that they are intentionally limiting - for simplicity. We could allow you to do anything and everything, but then that's already allowed by simply implementing your own router that implements |
When wanting authorization on every page except a login page it is a lot of repetitive work to have to compose every action, and if writing a module it is too much to expect someone to have to use a custom router. I was going to implement a security model I made in C# but it seems Play just isn't up to the job. |
Is it really repetitive to compose every action? You define a custom action builder once, and then instead of every action using the built in action builder, you make every action use the custom action builder, eg: def index = Action {
Ok
} becomes def index = Authenticated {
Ok
} How is that repetitive? You seem to be set on a particular solution, rather than set on solving a problem. Of course the solution you implemented for one particular framework won't transfer 1:1 to another particular framework, because those are different frameworks. But that doesn't mean that the problem you want to solve can't be solved in both frameworks, it just means they have different solutions. |
All of this is very nice and workable, but completely unavailable from Java code. Being able to access the path parameters from Java would make a whole class of Action compositions possible in Java. Even the official documentation shows something of the like (but then ducks out of actually showing how to get the user parameter) in https://www.playframework.com/documentation/2.4.x/JavaActionsComposition#Passing-objects-from-action-to-controller What exactly would be the drawback of carrying an array with all the URL parameters somewhere? |
One more note: It does not seem to be a huge amount of code considering that this can be implemented/hacked in about 30 lines when using Scala. As shown here: https://alots.wordpress.com/2014/05/01/accessing-url-parameters-as-get-parameters-in-play/ Also note that there are now quite a few indications that this would be a useful feature. |
I found a way of accessing Route. Check this |
In order for plugins to know more information about the incoming request could PlayFramework alter the Router.Routes trait so that it is possible to get the Method and PathPattern?
The intent is for the Global object to be able to use onRequestReceived to extract values from the request's URL
GET /x/:name/:age controllers.Application.tester(name: String, age: Int)
I could then get a list of parameters, their types, and their values as per the current request.
The text was updated successfully, but these errors were encountered: