読者です 読者をやめる 読者になる 読者になる

@tako_programingの忘備録とか

@tako_programingの忘備録とかです。技術系の話が多くなるのかも。

playのHTTP routingをクソ和訳

完全な自分用メモです。翻訳がおかしければ最終的な日本語もおかしいHTTP routingの和訳です。


ビルトインHTTPルーター

このルーターは、各HTTPリクエストをActionに変換する役割を果たすコンポーネントです。

HTTPリクエストはMVCフレームワークで、イベントとして認識されます。イベントには以下の2つの主要な情報が含まれています。

  • (e.g. /clients/1542, /photos/list)のようなクエリ文字列を含むリクエストパス

  • (e.g. GET, POST, …)のようなHTTPメソッド

ルーティングは、conf/routesファイルで定義されコンパイルされます。つまり、ブラウザ上にそのままエラーが表示されます。

Dependency Injection(依存性の注入)

playでは二種類のルーターの生成をサポートしています。1つは依存性注入ルータ、もう1つは静的ルーターです。標準では依存性注入ルーターが使われます。playでは依存性注入コントローラーを使用することを推奨していますので、Activatorのテンプレートでも依存性注入コントローラーを使用するようになっています。静的コントローラを使用する必要がある場合は、build.sbtに以下のように記述します。

routesGenerator := StaticRoutesGenerator

playのドキュメントのコードサンプルは、注入されたルータージェネレーターを使用することを前提としています。これを使用しない時は、ルートコントローラの呼び出しの先頭部に@シンボルをつけたり、各コントローラをclassではなくobjectとして宣言することで静的ルートジェネレータに自由に変更することが可能です。

ルーティングファイルの文法

conf/routeは、ルーティングに使用される設定ファイルです。このファイルには、playアプリケーションの全てのルートが一覧で表示されます。各ルートはHTTPメソッドとURIパターンで構成され、どちらもActionジェネレータへの呼び出しに関連付けれらます。

ルート定義は次のように記述します。:

GET   /clients/:id          controllers.Clients.show(id: Long)

ルートはHTTPメソッドで初まり、その後にURIパターンが続きます。最後の要素は呼び出し定義です。

また、ルートファイルには、#シンボルから始まる文字列でコメントを追加することができます。

# Display a client.
GET   /clients/:id          controllers.Clients.show(id: Long)

特定のプレフィックスの下にある別のルータを使用するには->の後ろに与えられた接頭辞を使用します。

->      /api                        api.MyRouter

この方法は、SIRDルーティングとも呼ばれるString Interpolationg Routing DSLと組み合わせたり、複数のルートファイルを使用してルーティングするSub Projectを処理する場合に便利です。

HTTPメソッド

HTTPメソッドは、HTTP(GETPATCHPOSTPUTDELETEHEAD)で、サポートされている有効なメソッドのうちどれでも構いません。

URIパターン

URIパターンはルートのリクエストパスを定義します。リクエストパスの一部は動的であっても構いません。

静的なパス

たとえば、GET /clients/allリクエストに対応させるためには、次のようなルートを定義します。

GET   /clients/all          controllers.Clients.list()

動的なパス

IDでクライアントを取得するようなルートを定義する場合は、動的なパーツを追加する必要があります。

GET   /clients/:id          controllers.Clients.show(id: Long)

注: URIパターンは複数の動的部分を持つこともできます。

動的部分のデフォルトのマッチングでは、正規表現[^/]+で定義されます。つまり,:idで定義された動的部分は、正確に1つのURIパスセグメントに一致します。他のパターンタイプとは異なり、パスセグメントはルートに自動的にURIデコードされた後、コントローラに渡され、逆ルートでエンコードされます。

複数の動的部分 /

動的部分が複数のURIパスセグメントをスラッシュで区切って取得するようにするには、*id構文(ワイルドカードパターンとも呼ばれる*id正規表現を使用すます。)を使って動的パターンを定義できます。

GET   /files/*name          controllers.Application.download(name)

GET /files/images/logo.pngのようなリクエストの場合、動的部分nameimages/logo.pngを取得します。

複数の/にまたがる動的部分は、ルータによってデコードされないまたは、逆ルータによってエンコードされることに注意する必要があります。生のURIセグメントを検証するのはあなたの責任です。逆ルータは文字列の連結のみを行うため、結果のパスが有効であることを確認する必要があります。例えば、複数のスラッシュやASCII以外の文字は含まれません。

動的部分にカスタム正規表現を使用する

$id <regex>構文を用いて動的部分に独自の正規表現を定義することができます。

GET   /items/$id<[0-9]+>    controllers.Items.show(id: Long)

ワイルドカードのルーティングと同様に、パターメタはルーターによっってデコードされないまたは、逆ルーターによってエンコードされません。それが正しいかどうかを検証する必要があります。

ActionGeneratorメソッドの呼び出し

ルートの定義に最後の部分はActionGeneratorメソッドの呼び出しです。この部分では、play.api.mvc.Action型の値を返すメソッドへの有効な呼び出しを定義する必要があります。これは通常、ControllerのActionメソッドになります。 メソッドがパラメータを定義していない場合は、メソッドの完全修飾名を指定してください。

GET   /                     controllers.Application.homePage()

Actionメソッドが1つ以上のパラメータを定義する場合、これらのパラメータはすべてリクエストURI、またはクエリ文字列から検索されます。

# Extract the page parameter from the path.
GET   /:page                controllers.Application.show(page)

または、

# Extract the page parameter from the query string.
GET   /                     controllers.Application.show(page)

下に、対応するControllerのめメソッド定義を示します。

def show(page: String) = Action {
  loadContentFromDatabase(page).map { htmlContent =>
    Ok(htmlContent).as("text/html")
  }.getOrElse(NotFound)
}

### パラメータの型 ルート定義にあるActionGeneratorメソッドの引数がString型の場合、型を明示する必要はありません。ですが、playでリクエストパラメータを他の特定のScalaタイプにする必要がある場合は、引数の型を明示する必要があります。 scala GET /clients/:id controllers.Clients.show(id: Long) また、Controllerの対応するshowメソッドの定義でも同様に型を明示する必要があります。 scala def show(id: Long) = Action { Client.findById(id).map { client => Ok(views.html.Clients.display(client)) }.getOrElse(NotFound) } ### パラメータの値の固定 リクエストパラメータに値が存在しない場合に使用するデフォルトの値を設定することもできます。 scala # Pagination links, like /clients?page=3 GET /clients controllers.Clients.list(page: Int ?= 1) ### オプションパラメータ 全てのリクエストに対して必ずしも必要とは限らないオプションのパラメータを指定することも可能です。

# The version parameter is optional. E.g. /api/list-all?version=3.0
GET   /api/list-all         controllers.Api.list(version: Option[String])

ローティングの優先順位

多くのルーティング定義がたくさんのリクエストに合致してしまう場合が存在します。その場合は、宣言順で使用されます。

逆ルーティング

ルーターは、逆にScalaプログラムからURLを生成するためにも使うことができます。この機能によって全てのURIパターンを単一の設定ファイルに集中させられるので、リファクタリングが楽になります。

ルーターはルートファイルで使用される各Controllerに対し、routesパッケージ内にreverse controllerを生成します。これは、ルートファイルで使われるメソッドと同一のシグネチャを持ちますが、play.api.mvc.Actionではなくplay.api.mvc.Callを返します。

たとえば、下のようなControllerを定義したとします。

package controllers

import play.api._
import play.api.mvc._

class Application extends Controller {

  def hello(name: String) = Action {
    Ok("Hello " + name + "!")
  }

}

これに対応するようにconf/routesにルーティングを記述すると

# Hello action
GET   /hello/:name          controllers.Application.hello(name)

次に以下のようにして、controller.routes.Applicationリバースコントローラーを使って、helloアクションメソッドを使うことができます。

// Redirect to /hello/Bob
def helloBob = Action {
  Redirect(routes.Application.hello("Bob"))
}

注: 各コントローラにはroutesサブパッケージが存在します。そのため、コントローラcontrollers.admin.Application.helloアクションは、controllers.admin.routes.Application.hello(ルートファイルconf/routesに、Scalaプログラム内の逆ルーティングで生成されたパスと一致する他のルートがない場合に限り)逆ルーティングを行うことが可能です。

Scalaプログラム内から呼ばれた逆Actionメソッドはとても単純に機能します。このメソッドは、パラメータをとり、ルートパターンに戻します。パスセグメント(:fooのような形をしたもの)は、値は実際に置き換えが行われる前の時点でエンコードされます。正規表現ワイルドカードを用いたルーティングパターンの場合は、値が複数のセグメントにまたがる可能性があります。そのため、そのような値を逆ルーティングに用いる場合には、必要に応じてこれらの値をエスケープして、未検証のユーザー入力を渡さないように注意してください。

デフォルトのコントローラ

playには、いくつかの便利なActionが定義されているDefault Controllerが含まれています。これらはルートファイルconf/routesから直接呼び出すことが可能です。

# Redirects to https://www.playframework.com/ with 303 See Other
GET   /about      controllers.Default.redirect(to = "https://www.playframework.com/")

# Responds with 404 Not Found
GET   /orders     controllers.Default.notFound

# Responds with 500 Internal Server Error
GET   /clients    controllers.Default.error

# Responds with 501 Not Implemented
GET   /posts      controllers.Default.todo

上記の例では、GET /のリダイレクトで、外部サイトに飛ぶようになっていますが、別のアクション(例では/ postsなど)にリダイレクトすることも可能です。

カスタムルーティング

playには、String Interpolating Routing DSL、通称sirdと呼ばれる組み込みルータを定義するためのDSLを定義されています。このDSLには、軽量のplayサーバーの組み込み、通常のplayアプリケーションへの高度なルーティング機能の提供、テスト用RESTモックサーバーを作成するなどの用途に使用できます。

詳しくはString Interpolationg Routing DSL