JavaでのPlayアプリケーションのルーティング
1概要
この記事では、http://playframework.com[Play Framework]を使用してWebアプリケーションを開発する際のルーティングの側面を探ります。
ルーティングはhttps://spring.io[Spring MVC]を含むほとんどのWeb開発フレームワークに見られる共通の概念です。
ルートはハンドラにマッピングされたURLパターンです。ハンドラーは、Webアプリケーションのダウンロード可能アセットなどの物理ファイル、またはMVCアプリケーションのコントローラーなどの要求を処理するクラスです。
2セットアップ
まず、Java Playアプリケーションを作成する必要があります。あなたのマシンにPlay Frameworkをセットアップする方法の詳細は私の紹介記事にあります。次のリンクから読むことができます:/java-intro-to-the-play-framework[このリンク]。
セットアップの終わりまでに、私たちはブラウザからアクセスできる実用的なPlayアプリケーションを持っているはずです。
3 HTTPルーティング
HTTPリクエストを送信するたびに、Playがどのコントローラに相談するべきかをどのように知っているか疑問に思うかもしれません。答えは
webapp/conf/routes
設定ファイルにあります。
PlayのルーターはHTTPリクエストをアクションコールに変換します。 HTTP要求はMVCアーキテクチャのイベントであると見なされ、ルータはどのコントローラおよびそのコントローラ内のどのアクションを実行するかについてrouteファイルを調べることによってそれらに反応します。
これらの各イベントは、2つのパラメータ、つまりクエリ
String
を持つリクエストパスとリクエストのHTTPメソッドをルータに提供します。
4 Playを使った基本的なルーティング
ルータが機能するためには、
conf/routes
ファイルでHTTPメソッドとURIパターンのマッピングを適切なコントローラアクションに定義する必要があります。
GET / controllers.HomeController.index
GET / count controllers.CountController.count
GET / message controllers.AsyncController.message
GET / assets/** file controllers.Assets.versioned(path="/public", file: Asset)
すべてのルートファイルは、
/assets
エンドポイント上のクライアントが使用できる
webapp/public
フォルダ内の静的リソースもマップする必要があります。 HTTPルートを定義する構文、およびHTTPメソッド
space
URIパターン
space
controllerアクションに注目してください。
5 URIパターン
このセクションでは、URIパターンについて少し説明します。
5.1. 静的URIパターン
上記の最初の3つのURIパターンは静的です。これは、リソースへのURLのマッピングが、コントローラーのアクションでそれ以上処理されずに行われることを意味します。
コントローラメソッドが呼び出される限り、リクエストの前に内容が決定されている静的リソースを返します。
5.2. 動的URIパターン
上記のURIパターンの最後は動的です。つまり、これらのURIでリクエストを処理するコントローラのアクションは、レスポンスを決定するためにリクエストからの情報を必要とします。上記の場合、ファイル名が必要です。
通常のイベントシーケンスでは、ルータはイベントを受信し、URLからパスを選択し、そのセグメントをデコードしてそれらをコントローラに渡します。
これが、パスおよびクエリパラメータがパラメータとしてコントローラアクションに注入されるポイントです。次のセクションの例でこれを実演します。
6. Play
による高度なルーティング
このセクションでは、動的URIパターンを使ったルーティングの高度なオプションについて詳しく説明します。
6.1. 単純パスパラメータ
これらはホストとポートの後に現れる要求URL内の名前のないパラメータで、出現順に解析されます。
webapp/app/HomeController.java
内に、次のように新しいアクションを作成しましょう。
public Result greet(String name) {
return ok("Hello " + name);
}
リクエストURLからパスパラメータを選び、それを変数名にマッピングできるようにしたいと思います。パスパラメータはURLで名前が付けられていないことを忘れないでください。
ルータはこれらの値をルート設定から取得します。
それでは、
webapp/conf/routes
を開き、この新しいアクションのマッピングを作成しましょう。
GET /greet/:name controllers.HomeController.greet(name)
コロンという構文を使って名前が動的パスセグメントであることをルータに通知し、それをgreetアクション呼び出しにパラメータとして渡す方法を説明します。
ブラウザに
http://locahost:9000/greet/john
をロードすると、名前が表示されます。
Hello john
そのため、アクションパラメータが文字列型の場合は、パラメータ型を指定せずにアクション呼び出し中に渡すことができますが、他の型では同じではありません。
/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,age:Integer)
String
ではないので、一番右側のアクション呼び出しでageパラメータタイプを指定する方法に注目してください。
変数を宣言するためのScalaの構文
age:Integer
にも注意してください。 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
です。これにより、クライアントは
webapp/public
フォルダからファイルをダウンロードできます。
http://localhost:9000/assets/images/favicon.png
をロードすると、
/public/images
フォルダにあるため、Play faviconの画像がブラウザに表示されます。
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パラメーターを受け取り、それをデコードするために独自のロジックを適用しています。この場合のロジックは、カンマ区切りの文字列を配列に分割することです。以前は、このデータをデコードするためにルーターに依存していました。
ワイルドカードを使って、私たちは自分自身の上にいます。このデータを渡す際に、クライアントが構文を正しく取得できることを望んでいます。
このアクションへのルートを作成しましょう。
GET /** data controllers.HomeController.introduceMe(data)
今度はURL
http://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
の下に配置して、新しい概念を紹介しましょう。このルーティング設定では、正規表現部分が正の整数であるURLだけが
squareMe
アクションにルーティングされます。
前の段落の説明に従ってルートを配置した場合は、
http://localhost:9000/square/2
をロードします。あなたは
ArrayIndexOutOfBoundsException
で迎えられるべきです:
リンク:/uploads/Capture-3.png%201336w[]
サーバーコンソールでエラーログを確認すると、アクション呼び出しは実際には
squareMe
アクションではなく
introduceMe
アクションに対して実行されたことがわかります。ワイルドカードについて前述したように、私たちは自分自身でいて、入ってくるデータを検証しませんでした。
そのため、
introduceMe
は、カンマ区切りの文字列ではなく、単一の文字列
“ 2”
で呼び出されました。分割した後、要素が1つだけの配列を取得しましたが、それでもインデックス
1
の要素にアクセスしようとしました。
しかし、なぜ
introduceMe
を呼び出しても、この呼び出しを
squareMe
にルーティングしたのでしょうか。その理由は、次にルーティングの優先度と呼ばれるPlay機能です。
7. ルーティング優先順位
squareMe
と
introduceMe
の間にあるように経路間に矛盾がある場合は、宣言順の最初の経路が取られます。
なぜ競合がありますか?ワイルドカードコンテキストパスのため、
/
data
は、ベースパス
/
以外のすべての要求URLに一致します。そのため、 URIパターンがワイルドカードを使用するすべての経路は、最後に** 出現する必要があります。
ここで、
introduceMe
ルートが
squareMe
の後にくるようにルートの宣言順序を変更してください。負荷:
2 Squared is 4
ルート内の正規表現の威力をテストするには、
http://locahost:9000/square/-1
をロードしてみてください。ルーターは
squareMe
ルートおよび
introduceMe
ルートの照合に失敗し、
ArrayIndexOutOfBoundsException
が再度取得されます。
これは、
-1
が提供された正規表現と一致せず、アルファベットも一致しないためです。
8パラメーター
ここまでは、パラメータについて説明した唯一の側面は、routesファイルでその型を宣言する方法です。
このセクションでは、ルート内のパラメータを扱うときに利用できるその他のオプションについて説明します。
8.1. 固定値を持つパラメータ
時には、パラメータに固定値を使用したいことがあります。これは、指定されたパスパラメータを使用するようにPlayに指示する方法、または要求コンテキストがpath
/
の場合は特定の固定値を使用する方法です。
別の見方をすると、2つのエンドポイント、つまり同じコントローラアクションにつながるコンテキストパスがあります。一方のエンドポイントは、要求URLからのパラメーターを要求し、そのパラメーターが存在しない場合はもう一方をデフォルトにします。
これを実証するために、
HomeController.index()
actionを次のように変更しましょう。
public Result index() {
return ok("Routing in Play by Baeldung");
}
私たちのAPIが常に
String
を返したくないと仮定します。
Routing in Play by Baeldung
リクエストに
author
パラメータが含まれていない場合にのみ、デフォルトで固定値
Baeldung
にデフォルト設定して、記事の著者の名前をリクエストと共に送信することでそれを制御したいのです。
それでは、パラメータを追加してインデックスアクションをさらに変更しましょう。
public Result index(String author) {
return ok("REST API with Play by "+author);
}
ルートに固定値パラメータを追加する方法も見てみましょう。
GET / controllers.HomeController.index(author="Baeldung")
GET /:author controllers.HomeController.index(author)
1つではなく
HomeController.index
アクションにつながる2つの別々のルートがあることに注目してください。
ブラウザから
http://localhost:9000/
をロードすると、次のようになります。
Routing in Play by Baeldung
http://localhost:9000/john
をロードすると、次のようになります。
Routing in Play by john
8.2. デフォルト値を持つパラメータ
固定値を持つこととは別に、パラメータは私たちがデフォルト値と呼ぶものを持つこともできます。要求が必要な値を提供しない場合は、どちらもコントローラーのアクションパラメーターに代替値を提供します。
この2つの違いは、** 固定値はパスパラメータのフォールバックとして使用され、デフォルト値はクエリパラメータのフォールバックとして使用されることです。
http://localhost:9000/param1/param2
のようなパスパラメータと__http://localhost:9000/?param1 = value1のようなクエリパラメータ
2つ目の違いは、2つをルートで宣言する構文にあります。
固定値パラメータは、以下のように代入演算子を使用します。
author="Baeldung"
デフォルト値は異なるタイプの代入を使いますが:
author ?= "Baeldung"
author
に値が含まれていない場合は、条件付きで
Baeldung
を
author
に割り当てる
?=
演算子を使用します。
完全なデモを行うには、
HomeController.index
アクションに戻りましょう。たとえば、パスパラメータである著者名とは別に、リクエストで渡されない場合はデフォルトで
1
になるはずのクエリパラメータとしてauthor
id
を渡したいとします。
インデックスアクションを次のように変更します。
public Result index(String author,int id) {
return ok("Routing in Play by:"+author+" ID:"+id);
}
そしてインデックスは以下にルーティングします。
GET / controllers.HomeController.index(author="Baeldung",id:Int?=1)
GET /:author controllers.HomeController.index(author,id:Int?=1)
ロード中の
http://localhost:9000/
:
Routing in Play by:Baeldung ID:1
http://localhost:9000/?id
= 10を押しています:
Routing in Play by:Baeldung ID:10
http://localhost:9000/john
:はどうでしょうか。
Routing in Play by:john ID:1
Routing in Play by:john ID:5
9結論
この記事では、ルーティング・イン・プレイ・アプリケーションの概念について説明しました。
このリンクをたどることで、私の他の記事、Play Frameworkを使ったREST APIもチェックすることができます。
プロジェクト全体のソースコードはhttps://github.com/eugenp/tutorials/tree/master/play-framework[on GitHub]から入手できます。