ドキュメント

§文字列補間ルーティングDSL

Playは、文字列補間ルーティングDSL(略してsird)と呼ばれる、埋め込みルーターを定義するためのDSLを提供します。この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として抽出する一方で、Scala 2.10を使用する場合はq_?またはq_oがオプションのクエリパラメーターを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")
    }
}

上記のidInt型です。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")
    }
}

これらが単なる通常の抽出オブジェクトであることをさらに説明するために、ここでは、@構文やif文など、case文の他のすべての機能を使用できることを示しています。

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 Routerのバインディング

アプリケーションでsird Routerを使用するように構成する方法は、ユースケースに応じて複数あります。

§routesファイルからのSIRDルーターの使用

routesファイルコントローラーを使用する通常のPlayプロジェクトと組み合わせてルーティングDSLを使用するには、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ルーターの合成

Routesは部分関数であるため、複数のルーターを合成できます。したがって、ルートをより小さく、より焦点を絞ったルーターに分割し、後でアプリケーションルーターで合成できます。たとえば、上記の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の埋め込み

sirdルーターを使用してPlayサーバーを埋め込む例は、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ルーターの提供

GuiceベースのPlayアプリでは、GuiceApplicationLoaderProvider[Router]をオーバーライドすることで、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ルーターはroutesファイルよりも強力で、IDEからよりアクセスしやすくなっています。

§バインディングのオーバーライド

ルーターは、バインディングとモジュールで説明されているように、アプリケーションローダーでたとえばGuiceApplicationBuilderを使用して、カスタムルーターバインディングまたはモジュールでオーバーライドすることもできます。

次へ:Javascriptルーティング


このドキュメントに誤りを見つけましたか?このページのソースコードはこちらにあります。ドキュメントガイドラインを読んだ後、プルリクエストを送信してください。質問やアドバイスがありましたら、コミュニティフォーラムにアクセスして、コミュニティとの会話を開始してください。