§字符串插值路由 DSL
Play 提供了一个用于定义嵌入式路由器的 DSL,称为字符串插值路由 DSL,简称 sird。此 DSL 有很多用途,包括嵌入轻量级 Play 服务器,为常规 Play 应用程序提供自定义或更高级的路由功能,以及模拟 REST 服务以进行测试。
Sird 基于字符串插值提取器对象。正如 Scala 支持将参数插值到字符串中以构建字符串(以及任何对象),例如 s"Hello $to"
,相同的机制也可以用于从字符串中提取参数,例如在 case 语句中。
此 DSL 位于 play.api.routing.sird
包中。通常,您需要导入此包以及其他几个包
import play.api.mvc._
import play.api.routing._
import play.api.routing.sird._
一个简单的使用示例是
val router = Router.from {
case GET(p"/hello/$to") =>
Action {
Results.Ok(s"Hello $to")
}
}
在这种情况下,插值路径模式中的 $to
参数将提取单个路径段以供操作使用。GET
提取器提取使用 GET
方法的请求。它接受一个 RequestHeader
并提取相同的 RequestHeader
参数,它仅用作便捷过滤器。其他方法提取器,包括 POST
、PUT
和 DELETE
也受支持。
与 Play 的编译路由器一样,sird 支持匹配多路径段参数,这是通过在参数后缀添加 *
来完成的
val router = Router.from {
case GET(p"/assets/$file*") =>
Assets.versioned(path = "/public", file = file)
}
正则表达式也受支持,通过在参数后缀添加尖括号中的正则表达式来完成
val router = Router.from {
case GET(p"/items/$id<[0-9]+>") =>
Action {
Results.Ok(s"Item $id")
}
}
查询参数也可以提取,使用 ?
运算符对请求进行进一步提取,并使用 q
提取器
val router = Router.from {
case GET(p"/search" ? q"query=$query") =>
Action {
Results.Ok(s"Searching for $query")
}
}
虽然 q
将必需查询参数提取为 String
,但 q_?
或 q_o
(如果使用 Scala 2.10)将可选查询参数提取为 Option[String]
val router = Router.from {
case GET(p"/items" ? q_o"page=$page") =>
Action {
val thisPage = page.getOrElse("1")
Results.Ok(s"Showing page $thisPage")
}
}
同样,q_*
或 q_s
可用于提取多值查询参数的序列
val router = Router.from {
case GET(p"/items" ? q_s"tag=$tags") =>
Action {
val allTags = tags.mkString(", ")
Results.Ok(s"Showing items tagged: $allTags")
}
}
可以使用 &
运算符提取多个查询参数
val router = Router.from {
case GET(
p"/items" ? q_o"page=$page"
& q_o"per_page=$perPage"
) =>
Action {
val thisPage = page.getOrElse("1")
val pageLength = perPage.getOrElse("10")
Results.Ok(s"Showing page $thisPage of length $pageLength")
}
}
由于 sird 只是一个普通的提取器对象(通过字符串插值构建),它可以与任何其他提取器对象组合,包括进一步提取其子参数。Sird 为一些最常见的类型提供了开箱即用的有用提取器,即 int
、long
、float
、double
和 bool
val router = Router.from {
case GET(p"/items/${int(id)}") =>
Action {
Results.Ok(s"Item $id")
}
}
在上面,id
的类型为 Int
。如果 int
提取器无法匹配,那么当然,整个模式将无法匹配。
类似地,相同的提取器可以与查询字符串参数一起使用,包括多值和可选查询参数。在可选或多值查询参数的情况下,如果任何存在的参数无法绑定到类型,则匹配将失败,但不存在参数不会导致匹配失败。
val router = Router.from {
case GET(p"/items" ? q_o"page=${int(page)}") =>
Action {
val thePage = page.getOrElse(1)
Results.Ok(s"Items page $thePage")
}
}
为了进一步说明这些只是普通的提取器对象,您可以在这里看到您可以使用 case
语句的所有其他功能,包括 @
语法和 if 语句。
val router = Router.from {
case rh @ GET(
p"/items/${idString @ int(id)}" ?
q"price=${int(price)}"
) if price > 200 =>
Action {
Results.Ok(s"Expensive item $id")
}
}
§绑定 sird 路由器
根据用例,可以通过多种方式配置应用程序以使用 sird 路由器。
§从路由文件使用 SIRD 路由器
要将路由 DSL 与使用 路由文件 和 控制器 的常规 Play 项目结合使用,请扩展 SimpleRouter
package api
import javax.inject.Inject
import play.api.mvc._
import play.api.routing.sird._
import play.api.routing.Router.Routes
import play.api.routing.SimpleRouter
class ApiRouter @Inject() (controller: ApiController) extends SimpleRouter {
override def routes: Routes = {
case GET(p"/") => controller.index
}
}
将以下行添加到 conf/routes
-> /api api.ApiRouter
§组合 SIRD 路由器
您可以将多个路由器组合在一起,因为路由是偏函数。因此,您可以将路由拆分为更小、更集中的路由器,然后在应用程序路由器中组合它们。例如,考虑上面的 ApiRouter
和一个新的 SinglePageApplicationRouter
,例如
class SpaRouter @Inject() (controller: SinglePageApplicationController) extends SimpleRouter {
override def routes: Routes = {
case GET(p"/api") => controller.api
}
}
然后,您可以将两者组合起来,为您的应用程序创建一个完整的路由器。
class AppRouter @Inject() (spaRouter: SpaRouter, apiRouter: ApiRouter) extends SimpleRouter {
// Composes both routers with spaRouter having precedence.
override def routes: Routes = spaRouter.routes.orElse(apiRouter.routes)
}
§嵌入 Play
在 嵌入 Play 部分中可以找到使用 sird 路由器嵌入 Play 服务器的示例。
§提供 DI 路由器
如 应用程序入口点 和 提供路由器 中所述,可以向应用程序提供路由器。
class SirdAppLoader extends ApplicationLoader {
def load(context: Context) = {
new SirdComponents(context).application
}
}
class SirdComponents(context: Context) extends BuiltInComponentsFromContext(context) with NoHttpFiltersComponents {
lazy val router = Router.from {
case GET(p"/hello/$to") =>
Action {
Ok(s"Hello $to")
}
}
}
§使用 Guice 提供 DI 路由器
通过覆盖 GuiceApplicationLoader
和 Provider[Router]
,可以在基于 Guice 的 Play 应用程序中提供 SIRD 路由器。
class ScalaSimpleRouter @Inject() (val Action: DefaultActionBuilder) extends SimpleRouter {
override def routes: Routes = {
case GET(p"/") =>
Action {
Results.Ok
}
}
}
@Singleton
class ScalaRoutesProvider @Inject() (playSimpleRouter: ScalaSimpleRouter, httpConfig: HttpConfiguration)
extends Provider[Router] {
lazy val get = playSimpleRouter.withPrefix(httpConfig.context)
}
class ScalaGuiceAppLoader extends GuiceApplicationLoader {
protected override def overrides(context: ApplicationLoader.Context): Seq[GuiceableModule] = {
super.overrides(context) :+ (bind[Router].toProvider[ScalaRoutesProvider]: GuiceableModule)
}
}
SIRD 路由器比路由文件更强大,并且更容易被 IDE 访问。
§覆盖绑定
您也可以使用例如 GuiceApplicationBuilder 在应用程序加载器中提供路由器,以使用自定义路由器绑定或模块覆盖,如 绑定和模块 中所述。
下一步:Javascript 路由
发现此文档中的错误?此页面的源代码可以在这里找到 这里。在阅读 文档指南 后,请随时贡献拉取请求。有疑问或建议要分享?转到 我们的社区论坛 与社区开始对话。