如何从 Scala-Play 应用程序中的 URL 中提取路由变量的值?

Pet*_*ris 5 scala playframework-2.3

我正在为 Play 框架编写一个模块。在我的模块的一部分中,我有以下代码

abstract class SecurityFiltering extends GlobalSettings{
  override def onRequestReceived(request: RequestHeader) = {
    play.Logger.debug("onRequestReceived: " + request)
    super.onRequestReceived(request)
  }

  override def doFilter(next: RequestHeader => Handler): (RequestHeader => Handler) = {
    request => {
      play.Logger.debug("doFilter: " + request)
      super.doFilter(next)(request)
    }
  }

  override def onRouteRequest(request: RequestHeader): Option[Handler] = {
    play.Logger.debug("onRouteRequest: " + request)
    super.onRouteRequest(request)
  }
}
Run Code Online (Sandbox Code Playgroud)

在 doFilter 方法中,我能够确定以下有用信息

ROUTE_PATTERN = /x/$name<[^/]+>/$age<[^/]+>
ROUTE_CONTROLLER = controllers.Application
ROUTE_ACTION_METHOD = tester
ROUTE_VERB = GET
path = /x/hello
Run Code Online (Sandbox Code Playgroud)

除此之外,我还需要QueryString之前的URL 命名部分的值。因此,在我的测试应用程序中给出以下路线,我需要检索 Name=Pete 和 Age=41

localhost:9000/x/Pete/41
Run Code Online (Sandbox Code Playgroud)

Play 框架中肯定有一些代码已经做到了这一点,但我无法找到它。有人可以建议我如何实现这个目标,或者指出哪个 Play 类提取这些值?

Pet*_*ris 1

根据JRoper的 GitHub 问题响应

onRequestReceived 负责路由和标记请求,因此,它在第一次调用时不会有任何路由信息,只有在调用后才会有。

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)
}
Run Code Online (Sandbox Code Playgroud)

也就是说,通常有更好、更类型安全的方法来实现您想要实现的目标。如果您依赖于 URL 中的特定参数,那么过滤器就不是正确的选择,因为过滤器适用于所有请求,无论它们是否具有这些参数。相反,您可能应该使用操作组合或自定义操作生成器,如下所示:

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"))
}
Run Code Online (Sandbox Code Playgroud)