JavaでのPlayアプリケーションのルーティング

1。概要

ルーティングは、SpringMVCを含むほとんどのWeb開発フレームワークに見られる一般的な概念です。

ルートは、ハンドラーにマップされるURLパターンです。ハンドラーは、Webアプリケーションのダウンロード可能なアセットなどの物理ファイル、またはMVCアプリケーションのコントローラーなどの要求を処理するクラスにすることができます。

このチュートリアルでは、PlayFrameworkを使用してWebアプリケーションを開発する際のルーティングの側面について説明します。

2.セットアップ

まず、JavaPlayアプリケーションを作成する必要があります。マシン上でPlayFrameworkをセットアップする方法の詳細は、紹介記事に記載されています。

セットアップが完了するまでに、ブラウザーからアクセスできるPlayアプリケーションが機能するようになります。

3.HTTPルーティング

では、HTTPリクエストを送信するたびに、Playはどのコントローラーを参照するかをどのように知るのでしょうか。この質問への答えは、app / conf / routes構成ファイルにあります。

Playのルーターは、HTTPリクエストをアクションコールに変換します。HTTPリクエストはMVCアーキテクチャのイベントと見なされ、ルーターは、どのコントローラーのルートファイルと、そのコントローラーのどのアクションを実行するかを調べることで、HTTPリクエストに反応します。

これらの各イベントは、ルーターに2つのパラメーターを提供します。クエリ文字列を含む要求パスと要求のHTTPメソッドです。

4.Playを使用した基本的なルーティング

ルーターが機能するには、conf / routesファイルでHTTPメソッドとURIパターンの適切なコントローラーアクションへのマッピングを定義する必要があります。

GET / controllers.HomeController.index GET / assets/*file controllers.Assets.versioned(path="/public", file: Asset)

すべてのroutesファイルは、/ assetsエンドポイントのクライアントが使用できるplay-routing / publicフォルダー内の静的リソースもマップする必要があります。

HTTPルートを定義する構文と、HTTPメソッドスペースURIパターンスペースコントローラーアクションに注意してください。

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 /ルートを開いて、この新しいアクションのマッピングを作成しましょう。

GET /greet/:name controllers.HomeController.greet(name: String)

名前がコロン構文の動的パスセグメントであることをルーターに通知し、それをパラメーターとしてgreetアクション呼び出しに渡す方法に注目してください。

それでは、ブラウザに// 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: String, age: Integer)

変数age:Integerを宣言するためのScala構文にも注意してください。Javaでは、整数の年齢構文を使用します。PlayFrameworkはScalaに組み込まれています。その結果、多くのscala構文があります。

// localhost:9000 / greet / john / 26をロードしましょう:

Hello john, you are 26 years old

6.2。パスパラメータのワイルドカード

ルート構成ファイルでは、最後のマッピングは次のとおりです。

GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)

We use a wildcard in the dynamic part of the path. What we are telling Play is that whatever value replaces *file in the actual request should be parsed as a whole and not decoded like in other cases of path parameters.

In this example, the controller is a built-in one, Assets, which allows the client to download files from the play-routing/public folder. When we load //localhost:9000/assets/images/favicon.png, we should see the image of the Play favicon in the browser since it's present in the /public/images folder.

Let's create our own example action in HomeController.java:

public Result introduceMe(String data) { String[] clientData = data.split(","); return ok("Your name is " + clientData[0] + ", you are " + clientData[1] + " years old"); }

Notice that in this action, we receive one String parameter and apply our logic to decode it. In this case, the logic is to split a comma-delimited String into an array. Previously, we depended on a router to decode this data for us.

With wildcards, we are on our own. We're hoping that the client gets our syntax correct while passing this data in. Ideally, we should validate the incoming String before using it.

Let's create a route to this action:

GET /*data controllers.HomeController.introduceMe(data)

Now load the URL //localhost:9000/john,26. This will print:

Your name is john, you are 26 years old

6.3. Regex in Path Parameters

Just like wildcards, we can use regular expressions for the dynamic part. Let's add an action that receives a number and returns its square:

public Result squareMe(Long num) { return ok(num + " Squared is " + (num * num)); }

Now we'll add its route:

GET /square/$num controllers.HomeController.squareMe(num:Long)

Let's place this route below the introduceMe route to introduce a new concept. We can only handle routes where the regex part is a positive integer with this routing configuration.

Now if we have placed the route as instructed in the previous paragraph, and we load //localhost:9000/square/2, we should be greeted with an ArrayIndexOutOfBoundsException:

If we check the error logs in the server console, we will realize that the action call was actually performed on introduceMe action rather than squareMe action. As said earlier about wildcards, we are on our own and we did not validate incoming data.

Instead of a comma-delimited string, the introduceMe method was called with the string “square/2“. Consequently, after splitting it, we got an array of size one. Trying to reach index 1 then threw the exception.

Naturally, we would expect the call to be routed to the squareMe method. Why was it routed to introduceMe? The reason is a Play feature we'll cover next called Routing Priority.

7. Routing Priority

If there is a conflict between routes as there is between squareMe and introduceMe, then Play picks the first route in declaration order.

Why is there a conflict? Because of the wildcard context path /*data matches any request URL apart from the base path /. So every route whose URI pattern uses wildcards should appear last in order.

Now let's change the declaration order of the routes such that the introduceMe route comes after squareMe and reload:

2 Squared is 4

To test the power of regular expressions in a route, try loading //locahost:9000/square/-1, a router will fail to match the squareMe route. Instead, it will match introduceMe, and we'll get the ArrayIndexOutOfBoundsException again.

This is because -1 does not match by the provided regular expression, neither does any alphabetic character.

8. Parameters

Up until this point, we've covered the syntax for declaring parameter types in the routes file.

In this section, we'll look at more options available to us when dealing with parameters in routes.

8.1. Parameters With Fixed Values

Sometimes we'll want to use a fixed value for a parameter. This is our way of telling Play to use the path parameter provided or if the request context is the path /, then use a certain fixed value.

Another way of looking at it is having two endpoints or context paths leading to the same controller action — with one endpoint requiring a parameter from the request URL and defaulting to the other in case the said parameter is absent.

To demonstrate this, let's add a writer() action to the HomeController:

public Result writer() { return ok("Routing in Play by Baeldung"); }

Assuming we don't always want our API to return a String:

Routing in Play by Baeldung

We want to control it by sending the name of an author of the article along with the request, defaulting to the fixed value Baeldung only if the request does not have the author parameter.

So let's further change the writer action by adding a parameter:

public Result writer(String author) { return ok("REST API with Play by " + author); }

Let's also see how to add a fixed value parameter to the route:

GET /writer controllers.HomeController.writer(author = "Baeldung") GET /writer/:author controllers.HomeController.writer(author: String)

Notice how we now have two separate routes all leading to the HomeController.index action instead of one.

When we now load //localhost:9000/writer from the browser we get:

Routing in Play by Baeldung

And when we load //localhost:9000/writer/john, we get:

Routing in Play by john

8.2. Parameters With Default Values

Apart from having fixed values, parameters can also have default values. Both provide fallback values to the controller action parameters in case the request does not provide the required values.

The difference between the two is that fixed values are used as a fallback for path parameters while default values are used as a fallback for query parameters.

Path parameters are of the form //localhost:9000/param1/param2 and query parameters are of the form //localhost:9000/?param1=value1¶m2=value2.

The second difference is in the syntax of declaring the two in a route. Fixed value parameters use the assignment operator as in:

author = "Baeldung"

While default values use a different type of assignment:

author ?= "Baeldung"

We use the ?= operator which conditionally assigns Baeldung to author in case author is found to contain no value.

To have a complete demonstration, let's create the HomeController.writer action. Let's say, apart from the author's name which is a path parameter, we also want to pass author id as a query parameter which should default to 1 if not passed in the request.

We'll change writer action to:

public Result writer(String author, int id) { return ok("Routing in Play by: " + author + " ID: " + id); }

そして、ライターのルートに:

GET /writer controllers.HomeController.writer(author="Baeldung", id: Int ?= 1) GET /writer/:author controllers.HomeController.writer(author: String, id: Int ?= 1)

// localhost:9000 / writerを読み込んでいます。

Routing in Play by: Baeldung ID: 1

打球// localhostを:?9000 /ライタID = 10が私たちを与えます:

Routing in Play by: Baeldung ID: 10

何について// localhostを:9000 /ライター/ジョン

Routing in Play by: john ID: 1

そして最後に、// localhost:9000 / writer / john?id = 5は次を返します。

Routing in Play by: john ID: 5

9.結論

この記事では、Playアプリケーションでのルーティングの概念について説明しました。PlayFrameworkを使用したRESTfulAPIの構築に関する記事もあり、このチュートリアルのルーティングの概念が実際の例に適用されています。

いつものように、このチュートリアルのソースコードはGitHubから入手できます。