JavaでのPlayアプリケーションのルーティング
1. 概要
ルーティングは、Spring MVCを含むほとんどのWeb開発フレームワークに見られる一般的な概念です。
ルートは、ハンドラーにマップされるURLパターンです。 ハンドラーは、Webアプリケーションのダウンロード可能なアセットなどの物理ファイル、またはMVCアプリケーションのコントローラーなどのリクエストを処理するクラスです。
このチュートリアルでは、Play Frameworkを使用してWebアプリケーションを開発する際のルーティングの側面について説明します。
2. セットアップ
まず、JavaPlayアプリケーションを作成する必要があります。 マシン上でPlayFrameworkをセットアップする方法の詳細は、introductory articleで入手できます。
セットアップが終了するまでに、ブラウザーからアクセスできる機能するPlayアプリケーションが必要です。
3. HTTPルーティング
では、HTTPリクエストを送信するたびに、Playはどのコントローラーを調べるべきかをどのように知るのでしょうか? この質問に対する答えは、app/conf/routes構成ファイルにあります。
Playのルーターは、HTTPリクエストをアクションコールに変換します。 HTTP requests are considered to be events in MVC architectureとルーターは、routesファイルを参照して、どのコントローラーとそのコントローラー内のどのアクションを実行するかを調べます。
これらの各イベントは、クエリ文字列を含むリクエストパスとリクエストのHTTPメソッドの2つのパラメータをルーターに提供します。
4. Playを使用した基本的なルーティング
ルーターが機能するには、conf/routesファイルでHTTPメソッドとURIパターンの適切なコントローラーアクションへのマッピングを定義する必要があります。
GET / controllers.HomeController.index
GET / assets/*file controllers.Assets.versioned(path="/public", file: Asset)
すべてのルートファイルは、/assetsエンドポイントのクライアントが使用できるplay-routing/publicフォルダー内の静的リソースもマップする必要があります。 HTTPルートを定義する構文と、HTTPメソッドspaceのURIパターンspaceのコントローラーアクションに注意してください。
5. URIパターン
このセクションでは、URIパターンについて少し説明します。
5.1. 静的URIパターン
上記の最初の3つのURIパターンは静的です。 つまり、リソースへのURLのマッピングは、コントローラーアクションでさらに処理することなく行われます。
コントローラーメソッドが呼び出される限り、リクエストの前にコンテンツが決定される静的リソースを返します。
5.2. 動的URIパターン
上記の最後のURIパターンは動的です。 これは、これらのURIで要求を処理するコントローラーアクションが、応答を決定するために要求からの情報を必要とすることを意味します。 上記の場合、ファイル名が必要です。
通常のイベントシーケンスでは、ルーターがイベントを受信し、URLからパスを選択し、そのセグメントをデコードして、コントローラーに渡します。
その後、パスとクエリのパラメーターがパラメーターとしてコントローラーアクションに挿入されます。 次のセクションの例でこれを示します。
6. Playによる高度なルーティング
このセクションでは、動的URIパターンを使用したルーティングの高度なオプションについて詳しく説明します。
6.1. 単純なパスパラメータ
シンプルパスパラメーターは、ホストとポートの後に表示されるリクエストURL内の名前のないパラメーターであり、出現順に解析されます。
play-routing/app/HomeController.java内で、新しいアクションを作成しましょう。
public Result greet(String name) {
return ok("Hello " + name);
}
リクエストURLからパスパラメーターを選択し、変数名にマップできるようにしたいと思います。
ルーターは、ルート構成からこれらの値を取得します。
それでは、play-routing/conf/routesを開いて、この新しいアクションのマッピングを作成しましょう。
GET /greet/:name controllers.HomeController.greet(name: String)
コロン構文を使用して名前が動的パスセグメントであることをルーターに通知し、次にそれをパラメーターとしてgreetアクションコールに渡すことに注意してください。
それでは、ブラウザにhttp://locahost:9000/greet/johnをロードしてみましょう。名前で迎えられます:
Hello john
これは他のタイプでは同じではありませんが、if our action parameter is of string type, we may pass it during the action call without specifying the parameter typeが発生することがあります。
年齢情報で/greetエンドポイントにスパイスを加えましょう。
HomeControllerの挨拶アクションに戻り、次のように変更します。
public Result greet(String name, int age) {
return ok("Hello " + name + ", you are " + age + " years old");
}
そしてへのルート:
GET /greet/:name/:age controllers.HomeController.greet(name: String, age: Integer)
変数age: Integerを宣言するためのScala構文にも注意してください。 Javaでは、Integer age構文を使用します。 Play FrameworkはScalaで構築されています。 そのため、多くのscala構文があります。
http://localhost:9000/greet/john/26をロードしましょう:
Hello john, you are 26 years old
6.2. パスパラメータのワイルドカード
ルート設定ファイルでは、最後のマッピングは次のとおりです。
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
パスの動的部分でワイルドカードを使用します。 Playに伝えているのは、実際のリクエストで*fileを置き換える値はすべて全体として解析され、パスパラメータの他の場合のようにデコードされないようにする必要があるということです。
この例では、コントローラーは組み込みのAssetsであり、クライアントがplay-routing/publicフォルダーからファイルをダウンロードできるようにします。 http://localhost:9000/assets/images/favicon.pngをロードすると、Playファビコンの画像が/public/imagesフォルダーにあるため、ブラウザーに表示されます。
HomeController.javaで独自のアクション例を作成しましょう。
public Result introduceMe(String data) {
String[] clientData = data.split(",");
return ok("Your name is " + clientData[0] + ", you are " + clientData[1] + " years old");
}
このアクションでは、1つのStringパラメーターを受け取り、ロジックを適用してデコードすることに注意してください。 この場合のロジックは、コンマで区切られたStringを配列に分割することです。 以前は、このデータをデコードするためにルーターに依存していました。
ワイルドカードを使用すれば、私たちは独力です。 このデータを渡すときに、クライアントが構文を正しく取得することを望んでいます。 理想的には、we should validate the incoming String before using itです。
このアクションへのルートを作成しましょう:
GET /*data controllers.HomeController.introduceMe(data)
次に、URLhttp://localhost:9000/john,26をロードします。 これは印刷されます:
Your name is john, you are 26 years old
6.3. パスパラメータの正規表現
ワイルドカードのように、動的部分に正規表現を使用できます。 数値を受け取り、その平方を返すアクションを追加しましょう。
public Result squareMe(Long num) {
return ok(num + " Squared is " + (num * num));
}
次に、そのルートを追加します。
GET /square/$num<[0-9]+> controllers.HomeController.squareMe(num:Long)
このルートをintroduceMeルートの下に配置して、新しい概念を導入しましょう。 このルーティング構成では、正規表現部分が正の整数であるルートのみを処理できます。
ここで、前の段落の指示に従ってルートを配置し、http://localhost:9000/square/2をロードすると、ArrayIndexOutOfBoundsExceptionで迎えられるはずです。
サーバーコンソールでエラーログを確認すると、アクション呼び出しが実際にはsquareMeアクションではなくintroduceMeアクションで実行されたことがわかります。 ワイルドカードについて前述したように、私たちは独力であり、着信データを検証しませんでした。
カンマ区切りの文字列の代わりに、introduceMeメソッドが文字列「square/2」で呼び出されました。 その結果、それを分割した後、サイズ1の配列を得ました。 インデックス1 thenに到達しようとすると、例外がスローされました。
当然、呼び出しはsquareMeメソッドにルーティングされると予想されます。 なぜintroduceMeにルーティングされたのですか? その理由は、次に取り上げるRouting Priority.という再生機能です。
7. ルーティングの優先順位
squareMeとintroduceMeの間にあるように、ルート間に競合がある場合は、Play picks the first route in declaration orderになります。
なぜ対立があるのですか? ワイルドカードコンテキストのため、パス/dataは、ベースパス/以外のすべてのリクエストURLと一致します。 したがって、* URIパターンがワイルドカードを使用するすべてのルートは、最後に表示される必要があります。
次に、introduceMeルートがsquareMeの後に来るようにルートの宣言順序を変更して、リロードします。
2 Squared is 4
ルート内の正規表現の能力をテストするには、http://locahost:9000/square/-1をロードしてみてください。ルーターは、squareMeルートとの一致に失敗します。 代わりに、introduceMe,と一致し、ArrayIndexOutOfBoundsExceptionを再度取得します。
これは、-1が指定された正規表現と一致せず、アルファベット文字も一致しないためです。
8. パラメーター
これまで、routesファイルでパラメータタイプを宣言するための構文について説明してきました。
このセクションでは、ルートのパラメータを処理するときに利用できるその他のオプションについて説明します。
8.1. 固定値のパラメーター
パラメータに固定値を使用したい場合があります。 これは、提供されたパスパラメータを使用するようにPlayに指示する方法です。または、リクエストコンテキストがパス/の場合は、特定の固定値を使用します。
もう1つの見方は、同じコントローラーアクションにつながる2つのエンドポイントまたはコンテキストパスを持つことです。一方のエンドポイントは要求URLからのパラメーターを必要とし、上記パラメーターが存在しない場合は他方にデフォルト設定します。
これを示すために、HomeControllerにwriter()アクションを追加しましょう。
public Result writer() {
return ok("Routing in Play by example");
}
APIが常にStringを返すとは限らないと仮定します。
Routing in Play by example
リクエストと一緒に記事の作成者の名前を送信することで制御したいと思います。リクエストにauthorパラメータがない場合にのみ、デフォルトで固定値exampleになります。
それでは、パラメータを追加して、writerアクションをさらに変更しましょう。
public Result writer(String author) {
return ok("REST API with Play by " + author);
}
ルートに固定値パラメータを追加する方法も見てみましょう。
GET /writer controllers.HomeController.writer(author = "example")
GET /writer/:author controllers.HomeController.writer(author: String)
1つではなくすべてHomeController.indexアクションにつながる2つの別々のルートがあることに注意してください。
ブラウザからhttp://localhost:9000/writerをロードすると、次のようになります。
Routing in Play by example
そして、http://localhost:9000/writer/johnをロードすると、次のようになります。
Routing in Play by john
8.2. デフォルト値のパラメータ
固定値のほかに、パラメーターにデフォルト値を設定することもできます。 両方とも、要求が必要な値を提供しない場合に、コントローラーアクションパラメーターにフォールバック値を提供します。
2つの違いは、fixed values are used as a fallback for path parameters while default values are used as a fallback for query parametersです。
パスパラメータの形式はhttp://localhost:9000/param1/param2で、クエリパラメータの形式はhttp://localhost:9000/?param1=value1¶m2=value2です。
2番目の違いは、ルートで2つを宣言する構文です。 固定値パラメーターは、次のように割り当て演算子を使用します。
author = "example"
デフォルト値は異なるタイプの割り当てを使用しますが:
author ?= "example"
authorに値が含まれていないことが判明した場合に、条件付きでexampleをauthorに割り当てる?=演算子を使用します。
完全なデモンストレーションを行うために、HomeController.writerアクションを作成しましょう。 たとえば、パスパラメータである作成者の名前とは別に、クエリパラメータとして作成者idを渡したいとします。クエリパラメータは、リクエストで渡されない場合はデフォルトで1になります。
writerのアクションを次のように変更します。
public Result writer(String author, int id) {
return ok("Routing in Play by: " + author + " ID: " + id);
}
writerは次の場所にルーティングします。
GET /writer controllers.HomeController.writer(author="example", id: Int ?= 1)
GET /writer/:author controllers.HomeController.writer(author: String, id: Int ?= 1)
http://localhost:9000/writerをロードすると、次のようになります。
Routing in Play by: example ID: 1
http://localhost:9000/writer?id=10を押すと、次のようになります。
Routing in Play by: example ID: 10
Routing in Play by: john ID: 1
そして最後に、http://localhost:9000/writer/john?id=5 returns:
Routing in Play by: john ID: 5
9. 結論
この記事では、Playアプリケーションでのルーティングの概念を探りました。 また、building a RESTful API with Play Frameworkに関する記事があり、このチュートリアルのルーティングの概念が実際の例に適用されています。
いつものように、このチュートリアルのソースコードはover on GitHubで入手できます。