文档

§字符串插值路由 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 参数,它仅用作便捷过滤器。其他方法提取器,包括 POSTPUTDELETE 也受支持。

与 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 为一些最常见的类型提供了开箱即用的有用提取器,即 intlongfloatdoublebool

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 路由器

通过覆盖 GuiceApplicationLoaderProvider[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 路由


发现此文档中的错误?此页面的源代码可以在这里找到 这里。在阅读 文档指南 后,请随时贡献拉取请求。有疑问或建议要分享?转到 我们的社区论坛 与社区开始对话。