在 Day19,我們學會了如何建立一個回傳固定訊息 "Hello, World!" 的 API 端點 (Endpoint)。但真實世界的應用程式需要更靈活,它必須能夠根據使用者提供的不同資訊,來回傳不同的結果。
今天,我們就要來學習如何讓 API「動起來」,接收來自 URL 的動態參數。
完成本單元後,你將能夠:
@PathVariable
從 URL 路徑中 (如 /users/123
) 提取變數,用來指定特定資源。@RequestParam
從 URL 查詢字串中 (如 /search?keyword=java
) 提取參數,用來做資料的篩選、排序。@PathVariable
- 擷取 URL 路徑中的變數@PathVariable
?想像一下,你在設計一個查詢使用者資料的 API (Application Programming Interface)。你希望 URL (Uniform Resource Locator) 的結構是這樣的:
/users/1
:查詢 ID 為 1 的使用者/users/42
:查詢 ID 為 42 的使用者/products/apple
:查詢名稱為 "apple" 的商品你會發現,URL 中的 1
、42
、apple
都是變數。我們不可能為世界上每一個 ID 都手動寫一個 @GetMapping
。這時,@PathVariable
就登場了!
@PathVariable
的核心功能就是將 URL 路徑中的變數值,綁定 (Bind) 到你控制器 (Controller) 方法中的參數上。它讓我們可以建立這種動態、有彈性的 URL 結構。
簡單來說:
@PathVariable
就像一個模板取值工具,它能從 URL 的特定位置「挖」出一個值,並將其作為變數傳遞給你的 Java 程式碼。
@PathVariable
?使用 @PathVariable
非常直觀,只需要兩步驟:
{}
定義變數:在 @GetMapping
或其他請求註解 (Annotation) 的路徑中,使用 {變數名}
來宣告一個位置是動態的。@PathVariable
接收:在處理該請求的方法中,使用 @PathVariable
註解 (Annotation) 來告訴 Spring:「請把 URL 路徑上對應的值給我」。import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/users") // 將 /users 作為這個 Controller 的共同前綴
public class UserController {
// 1. 在路徑中定義變數 {id}
@GetMapping("/{id}")
// 2. 使用 @PathVariable 將 {id} 的值綁定到 Long id 參數上
public String getUserById(@PathVariable Long id) {
// 為了簡化,我們直接返回一個字串,實務上這裡會是查詢資料庫的邏輯
return "正在查詢 ID 為 " + id + " 的使用者資料...";
}
}
解析:
@GetMapping("/{id}")
:這裡的 {id}
就是我們定義的路徑變數。它告訴 Spring,URL 在 /users/
後面的那段內容是一個動態變數,我們把它命名為 id
。@PathVariable Long id
:@PathVariable
會自動去路徑中尋找同名的變數 ({id}
),並將其值賦予給方法參數 id
。Spring Boot 非常聰明,它還會自動幫你將 URL 中的字串 (例如 "123") 轉換成你指定的型別 (例如 Long
)。啟動程式並測試:
http://localhost:8080/users/1
=> 回應: 正在查詢 ID 為 1 的使用者資料...
http://localhost:8080/users/99
=> 回應: 正在查詢 ID 為 99 的使用者資料...
雖然不建議,但有時路徑變數名稱可能和你的方法參數名稱不同。@PathVariable
允許你明確指定要綁定哪個變數。
@GetMapping("/profile/{userId}")
public String getUserProfile(@PathVariable("userId") Long id) {
// 透過 @PathVariable("userId"),明確告訴 Spring
// 將路徑中的 {userId} 變數,賦值給名為 id 的參數
return "查詢使用者 ID 為 " + id + " 的個人資料。";
}
最佳實踐 (Best Practice): 為了程式碼的簡潔與可讀性,強烈建議保持路徑變數和方法參數名稱的一致性,這樣就可以省略
("userId")
,讓程式碼更乾淨。
@PathVariable
(進階)有時候,我們希望某個路徑變數是可選的,例如 /search
和 /search/keyword
都能對應到同一個方法。
重要提示:要讓 @PathVariable
可選,你必須提供兩個路徑映射:一個包含變數,另一個不包含。
import java.util.Optional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SearchController {
// 使用 Java 8 的 Optional<T> 是處理可選參數的現代且安全作法
// 1. 提供兩種路徑
@GetMapping({"/find", "/find/{query}"})
// 2. 將 required 設為 false,並用 Optional 包裝
public String findItems(@PathVariable(name = "query", required = false) Optional<String> query) {
if (query.isPresent()) {
return "使用 Optional 查找: " + query.get();
} else {
return "使用 Optional 查找所有項目...";
}
}
}
Optional<String>
的好處:它能清晰地表達「這個參數可能不存在」的意圖,並幫助你避免惱人的空指針異常 (NullPointerException)。@RequestParam
- 解析 URL 查詢參數@RequestParam
?當你在 Google 搜尋時,網址會變成像這樣: https://www.google.com/search?q=Spring+Boot
網址中 ?
後面的部分,如 q=Spring+Boot
,就是查詢參數 (Query Parameter)。它是一種 key=value
形式的組合,用來傳遞額外資訊,常見於篩選、排序、分頁等功能。
在 Spring Boot 中,@RequestParam
的工作就是將這些 URL 中的查詢參數,綁定到你控制器方法中的參數上。
簡單來說:
@RequestParam
就像一個鉤子,幫你把 URL 在?
後面的資料,輕鬆地「鉤」到你的 Java 程式碼裡。
@RequestParam
?import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
@GetMapping("/greeting")
public String greet(@RequestParam String name) {
return "哈囉, " + name + "!歡迎學習 Spring Boot!";
}
}
@RequestParam String name
:這行程式碼告訴 Spring:「請幫我從 URL 中找到一個叫做 name
的查詢參數,並把它的值放進 String name
這個變數裡。」啟動程式並測試:
http://localhost:8080/greeting?name=John
=> 回應: 哈囉, John!歡迎學習 Spring Boot!
@RequestParam
的常用屬性@RequestParam
提供了一些非常有用的屬性,讓我們的 API 更有彈性。
name
(或 value
):當 URL 參數名和 Java 變數名不同時使用。// URL 需使用 ...?username=Mary
@GetMapping("/greet-user")
public String greetUser(@RequestParam(name = "username") String name) {
return "Hi, " + name;
}
required
:設定參數是否為必需的,預設為 true
。// URL 可以不帶 name 參數
@GetMapping("/greet-optional")
public String greetOptional(@RequestParam(required = false) String name) {
if (name == null) {
return "哈囉, 陌生人!";
}
return "哈囉, " + name + "!";
}
測試:訪問 http://localhost:8080/greet-optional
=> 回應: 哈囉, 陌生人!
defaultValue
:提供預設值。當你設定了 defaultValue
,required
會自動變為 false
。
// 如果 URL 不帶 name 參數,name 變數的值會是 "訪客"
@GetMapping("/greet-default")
public String greetDefault(@RequestParam(defaultValue = "訪客") String name) {
return "哈囉, " + name + "!歡迎光臨!";
}
測試:訪問 http://localhost:8080/greet-default
=> 回應: 哈囉, 訪客!歡迎光臨!
@PathVariable
vs. @RequestParam
:何時該用誰?這是一個非常重要的觀念,也是初學者常混淆的地方。
特性 | @PathVariable (路徑變數) |
@RequestParam (查詢參數) |
---|---|---|
用途 | 識別一個特定的「資源 (Resource)」 | 對資源進行「篩選、排序或分頁」 |
URL 結構 | 乾淨、有階層性,是 URL 路徑的一部分 | 在 ? 之後,以 key=value 形式存在 |
語意 | "我要這個東西" | "我要符合這些條件的東西" |
範例 | /users/123 (取得 ID 為 123 的使用者) |
/products?category=books&page=1 (取得書籍類的第一頁商品) |
範例 | /orders/ORD-001 (取得訂單號 ORD-001) |
/users?status=active&sort=name_asc (取得所有活躍使用者,並依姓名升序排序) |
是否可選 | 通常是必需的 (因為它指定了資源) | 可選的居多 (篩選條件可有可無) |
恭喜你!現在你已經掌握了讓 API 變得動態的兩大利器。你不再只能建立回傳固定內容的 API,而是可以:
@PathVariable
建立一個 GET /users/{id}
的端點來查詢特定使用者。@RequestParam
建立一個 GET /products
的端點,並用 ?keyword=laptop
來搜尋商品,或用 ?sort=price
來排序。這些是建構真實世界 RESTful API 的基礎,你已經為後續更複雜的功能打下了堅實的根基!