知識庫 / REST RSS 訂閱

Java 應用中的路由

Jakarta EE,REST
HongKong
4
03:55 AM · Dec 06 ,2025

1. 概述

路由是一個在大多數 Web 開發框架中都出現的常見概念,包括 Spring MVC

路由是指將 URL 模式映射到處理器的 URL 模式。處理器可以是 Web 應用程序中的物理文件,例如可下載的資產,也可以是處理請求的類,例如 MVC 應用程序中的控制器。

在本教程中,我們將探討使用 Play Framework 開發 Web 應用程序時,路由的方面。

2. 部署

首先,我們需要創建一個 Java Play 應用程序。有關如何在機器上設置 Play Framework 的詳細信息,請參閲我們的入門文章。

完成部署後,我們應該能夠從瀏覽器訪問一個可運行的 Play 應用程序。

3. HTTP 路由

Play 如何知道在收到 HTTP 請求時應該諮詢哪個控制器?答案在於 app/conf/routes 配置文件的設置。

Play 的路由機制將 HTTP 請求轉換為對 action 的調用。 HTTP 請求被視為 MVC 架構中的事件,路由機制通過查閲 routes 文件來確定執行哪個控制器和該控制器的哪個 action。

每個事件都向路由器提供兩個參數:帶有查詢字符串的請求路徑以及請求的 HTTP 方法。

4. 使用 Play 進行基本路由

為了讓路由器發揮作用,<em/>conf/routes</em/> 文件必須定義 HTTP 方法和 URI 模式與適當的控制器動作之間的映射關係:

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

所有路由文件也必須使 play-routing/public 文件夾中的靜態資源對客户端在 /assets 端點可用。
請注意定義 HTTP 路由的語法,以及 HTTP 方法、URI 模式和控制器動作之間的關係。

5. URI 模式

本節將對 URI 模式進行更詳細的闡述。

5.1 靜態 URI 模式

上述前三個 URI 模式為靜態。這意味着 URL 與資源的映射在控制器操作中不會進行任何進一步的處理。

只要控制器方法被調用,它就會返回一個靜態資源,其內容在請求之前就已經確定。

5.2. 動態 URI 模式

上方的最後一個 URI 模式是動態的。這意味着為這些 URI 上的請求服務的控制器動作需要從請求中獲取一些信息以確定響應。在上述案例中,它期望一個文件名。

正常的事件序列是路由器接收一個事件,從 URL 中選擇路徑,解碼其片段,並將它們傳遞給控制器。

路徑和查詢參數隨後作為參數注入到控制器動作中。我們將在此後續部分中通過一個示例進行演示。

高級路由與Play

在這一部分,我們將詳細討論使用動態 URI 模式進行高級路由的選項。

6.1. 簡單路徑參數

簡單路徑參數是在請求 URL 中出現的,在主機和端口之後,按照出現的順序解析的無名參數。

<em >play-routing/app/HomeController.java</em > 中,讓我們創建一個新的動作:

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

事情是這樣發生的,如果我們的 action 參數是字符串類型,我們可以在調用 action 時無需指定參數類型地傳遞它,儘管這不適用於其他類型。

讓我們為 /greet 端點添加年齡信息。

回到 HomeController 的 greet action,我們將它更改為:

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)

請注意 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,它允許客户端從 play-routing/public 文件夾下載文件。當我們加載 http://localhost:9000/assets/images/favicon.png 時,我們應該在瀏覽器中看到 Play 的 favicon 圖像,因為它位於 /public/images 文件夾中。

讓我們在 HomeController.java 中創建一個自定義的 action:

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

請注意,在這一操作中,我們接收一個 String 參數並應用我們的邏輯對其進行解碼。在這種情況下,邏輯是將逗號分隔的 String 分割成一個數組。此前,我們依賴路由器來解碼這些數據。

使用通配符時,我們必須自己處理。我們希望客户端能夠正確地使用我們的語法傳遞這些數據。理想情況下,我們應該在使用前驗證傳入的 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 路由下方,以介紹一個新概念。 僅當此路由配置下,正則表達式部分為正整數時,我們才能處理路由。

現在,如果我們按照上一段中的指示放置了該路由,並加載 http://localhost:9000/square/2,我們應該收到一個 ArrayIndexOutOfBoundsException

如果我們在服務器控制枱檢查錯誤日誌,我們會發現該操作調用實際上是在 introduceMe 操作中執行的,而不是 squareMe 操作。 就像前面所説,關於通配符,我們自己負責,並且沒有驗證傳入的數據。

而不是逗號分隔的字符串,introduceMe 方法被調用時,傳入的字符串是 “square/2”。 因此,分割後,我們得到一個大小為一的數組。 嘗試訪問索引 1 導致拋出異常。

自然,我們期望該調用被路由到 squareMe 方法。 為什麼它被路由到 introduceMe? 這是我們將在下一部分介紹的 Play 功能 Routing Priority

7. 路由優先級

如果存在路由衝突,例如 squareMeintroduceMe 之間,則 Play 會選擇聲明順序中的第一個路由

為什麼會出現衝突?因為通配符上下文路徑 /*data 匹配所有請求 URL,除了基本路徑 /。因此,所有使用通配符的 URI 模式的路由應該在聲明順序中最後出現

現在,讓我們更改路由的聲明順序,使得 introduceMe 路由在 squareMe 路由之後,並重新加載:

2 Squared is 4

為了測試路由中正則表達式的強大功能,嘗試加載 http://locahost:9000/square/-1,路由器將無法匹配 squareMe 路由。相反,它會匹配 introduceMe,並且我們再次會得到 ArrayIndexOutOfBoundsException

這是因為 -1 不匹配提供的正則表達式,任何字母字符也不會匹配。

8. 參數

在此之前,我們已經介紹了在路由文件中聲明參數類型的語法。

在本節中,我們將探討在路由中處理參數時可用的更多選項。

8.1. 帶有固定值的參數

有時,我們可能希望為參數使用固定的值。 這樣做是為了告訴 Play 使用提供的路徑參數,或者如果請求上下文為路徑 ,則使用一個固定的值。

換句話説,我們有兩個端點或上下文路徑都指向相同的控制器動作——其中一個端點需要從請求 URL 中獲取參數,並在該參數不存在時,默認使用另一個端點。

為了演示這一點,讓我們為 HomeController 添加一個 writer() 動作:

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

假設我們不總是希望我們的API返回一個String:

Routing in Play by Baeldung

我們希望通過發送文章作者的名稱以及請求,默認情況下僅當請求中不包含 author 參數時,才使用固定的值 Baeldung 來控制它。

因此,讓我們進一步修改 writer 操作,添加一個參數:

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

讓我們也看看如何為路由添加一個固定值參數:

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

請注意,現在我們有兩個獨立的路由都指向 HomeController.index 動作,而不是之前的一個。

當我們現在從瀏覽器加載 http://localhost:9000/writer 時,我們得到:

Routing in Play by Baeldung

當我們加載 http://localhost:9000/writer/john, 我們得到:

Routing in Play by john

8.2. 帶有默認值的參數

除了具有固定值之外,參數也可以具有默認值。兩者都為控制器操作的參數提供備用值,當請求未提供所需的值時生效。

區別在於:固定值用作路徑參數的備用值,而默認值則用作查詢參數的備用值。

路徑參數的形式為 <em >http://localhost:9000/param1/param2</em>,查詢參數的形式為 <em >http://localhost:9000/?param1=value1&amp;param2=value2</em>

第二種差異在於在路由中聲明它們的語法。固定值參數使用賦值運算符,例如:

author = "Baeldung"

默認值使用一種不同的賦值方式:

author ?= "Baeldung"

我們使用 ?= 運算符,在 author 值為空時,條件地將 Baeldung 賦值給 author

為了進行完整的演示,讓我們創建 HomeController.writer 動作。 假設,除了作者姓名(作為路徑參數),我們還希望將作者 id 作為查詢參數傳遞,如果請求中未傳遞,則默認為 1

我們將 writer 動作更改為:

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)

現在正在加載 http://localhost:9000/writer ,我們看到:

Routing in Play by: Baeldung ID: 1

訪問 http://localhost:9000/writer?id=10 結果如下:

Routing in Play by: Baeldung ID: 10

關於 怎麼樣?

Routing in Play by: john ID: 1

最後,<em http://localhost:9000/writer/john?id=5返回:

Routing in Play by: john ID: 5

9. 結論

本文介紹了 Play 應用中的 Routing 概念。此外,我們還有一篇關於使用 Play Framework 構建 RESTful API 的文章,其中 Routing 概念在實踐示例中得到了應用。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.