Spring 中的 API 版本控制
1. 引言
在當今快節奏的軟體生態系統中,API是數位通訊的基礎。隨著應用程式的更新換代,API也必然隨之改變。然而,我們如何在不影響現有用戶的情況下解決問題或添加新功能?
API 版本控制是解決方案。最近,Spring Framework 和 Spring Boot 都原生支援 API 版本控制,讓這個過程比以往更方便。
在本教程中,我們將討論 Spring 中的 API 版本控制。
2. 為什麼 API 版本控制很重要以及最佳實務?
隨著應用程式的發展,API 也必然會發生變化,無論是新增功能、更改回應格式或解決不一致之處。
對於依賴舊合約的現有客戶而言,這些變更可能會造成服務中斷。 API版本控制透過保證向後相容性並允許創新,提供了一種系統化的方法來管理這種演變。
它降低了採用門檻,讓客戶能夠以自己的步調升級,並保持服務演進的透明度。
2.1. 最佳實踐
我們只需在真正必要時才應用版本控制,避免不必要的複雜性,以免給開發人員和客戶帶來負擔。任何變更都應清晰記錄,以便 API 的使用者了解哪些是新增的,哪些已被棄用。
同樣重要的是提供遷移路徑和時間表,確保棄用過程能夠平穩過渡,而不是突兀地終止。此外,還應部署自動化測試,以確保所有受支援的版本都能繼續如預期運作。
最終,所選的版本控制策略必須與業務需求和組織優先事項保持一致,在創新和穩定性之間取得適當的平衡。
3. 如何在 Spring Boot 中使用 API 版本控制?
讓我們探討如何使用 Java (17+) 和 Spring Boot (3.x 或 4.x) 在 Spring Boot 中實作常見的 API 版本控制策略。
3.1. URI 版本控制(基於路徑)
這種方法透過將版本號直接新增到 URL 路徑中,使版本控制方案清晰易懂。
開發者可以為每個版本維護不同的控制器,客戶端也可以輕鬆查看自己呼叫的是哪個版本。雖然這種方法流行且簡單易行,但它可能會使介面變得雜亂,有時也被認為不夠符合 RESTful 規範。
以下是如何在 Spring Boot 中實作 URI 版本控制的方法:
@RestController
@RequestMapping("/api/v1/users")
public class UserV1Controller {
@GetMapping
public String getUsersV1() {
return "User list from API v1";
}
}
@RestController
@RequestMapping("/api/v2/users")
public class UserV2Controller {
@GetMapping
public String getUsersV2() {
return "User list from API v2 with extra fields";
}
}
3.2 請求參數版本控制
請求參數版本控制允許客戶端在請求中使用查詢參數來指定 API 版本。這種方法易於使用,並且能夠確保所有版本的基本端點保持一致。
但是,這要求客戶端記住要包含該參數,如果省略,伺服器必須優雅地處理預設值或回退方案。
以下範例示範了請求參數版本控制:
@RestController
public class UserParamController {
@GetMapping("/api/users")
public Object getUsers(@RequestParam(name = "version", defaultValue = "1") String version) {
if ("1".equals(version)) {
return List.of("Alice", "Bob");
} else if ("2".equals(version)) {
return List.of(
new UserV2("Alice", "[email protected]", 30),
new UserV2("Bob", "[email protected]", 25)
);
}
return "Unsupported API version";
}
}
3.3. 頭部版本控制
頭部版本控制使用自訂請求頭來指示客戶端想要使用的 API 版本。這種方法可以保持 URL 的簡潔性,並將端點設計和版本控制問題分開。
這樣做的缺點是,客戶端必須了解並正確設定標頭,這會增加整合的複雜性。以下範例示範了標頭版本控制:
@RestController
public class UserHeaderController {
@GetMapping(value = "/api/users", headers = "X-API-VERSION=1")
public List<UserV1> getUsersV1() {
return List.of(
new UserV1("Alice"),
new UserV1("Bob")
);
}
@GetMapping(value = "/api/users", headers = "X-API-VERSION=2")
public List<UserV2> getUsersV2() {
return List.of(
new UserV2("Alice", "[email protected]", 30),
new UserV2("Bob", "[email protected]", 25)
);
}
}
3.4 內容協商
內容協商利用 Accept 標頭和自訂媒體類型來決定應該傳回哪個版本的 API。
這種方法靈活且符合 RESTful 原則,因為它允許同一資源有多種表示。缺點是需要仔細設定並就 MIME 類型達成一致,這可能會增加團隊管理的複雜性。
以下是我們實現內容協商的方法:
@RestController
@RequestMapping("/api/users")
public class UserContentNegotiationController {
@GetMapping(produces = "application/vnd.company.v1+json")
public String getUsersV1() {
return "User list v1";
}
@GetMapping(produces = "application/vnd.company.v2+json")
public String getUsersV2() {
return "User list v2";
}
}
3.5. 使用 Spring Boot 原生版本控制支持
Spring Framework 7 和 Spring Boot 4 透過註解引入了對 API 版本控制的原生支持,減少了對自訂解決方案的需求。
透過將版本控制直接整合到控制器定義中,這種標準化方法提高了專案間的一致性,並簡化了維護工作。
它還能與現有的請求映射和內容協商機制無縫集成,從而提升開發者體驗。以下是如何在 Spring Boot 中實現原生版本控制支援:
@RestController
public class UserMimeController {
public static final String V1_MEDIA = "application/vnd.example.users-v1+json";
public static final String V2_MEDIA = "application/vnd.example.users-v2+json";
@GetMapping(value = "/api/users", produces = V1_MEDIA)
public List<UserV1> usersV1() {
return List.of(
new UserV1("Alice"),
new UserV1("Bob")
);
}
@GetMapping(value = "/api/users", produces = V2_MEDIA)
public List<UserV2> usersV2() {
return List.of(
new UserV2("Alice", "[email protected]", 30),
new UserV2("Bob", "[email protected]", 25)
);
}
// Optional fallback
@GetMapping(value = "/api/users", produces = MediaType.APPLICATION_JSON_VALUE)
public List<UserV1> defaultUsers() {
return List.of(
new UserV1("Alice"),
new UserV1("Bob")
);
}
}
4. Spring Framework 7 和 Boot 4 的新特性
最新版本的 Spring 提供了卓越的 API 版本控制支持,例如對具有原生註解的控制器和端點進行版本控制。此外,它還透過規範團隊間的模式減少了不一致。
此外,整合工具增強了開發人員的體驗,提高了與 Spring 中的請求對應和內容協商的兼容性。
與自訂解決方案相比,Spring Framework 7 和 Spring Boot 4 透過註解提供的原生 API 版本控制支援顯著簡化了流程。
現在,我們無需在控制器中手動處理版本檢查,而是可以直接在方法層級使用 `@ApiVersion` 註解聲明版本。這種方法提高了程式碼可讀性,減少了樣板程式碼,並確保了團隊間的一致性。
以下範例展示如何使用原生註解定義相同端點的兩個版本:
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping
@ApiVersion("1")
public List usersV1() {
return List.of(
new UserV1("Alice"),
new UserV1("Bob")
);
}
@GetMapping
@ApiVersion("2")
public List usersV2() {
return List.of(
new UserV2("Alice", "[email protected]", 30),
new UserV2("Bob", "[email protected]", 25)
);
}
}
上述特性使得開發人員無需使用自訂版本控制方案。或者,我們可以依賴 Spring 的標準化方法,從而確保專案間的一致性。
5. Spring 中常見的 API 版本控制策略
下表詳細說明了 Spring 中常見的 API 版本控制策略:
| 策略 | 例子 | 優點 | 缺點 |
|---|---|---|---|
| URI 版本控制 | /api/v1/users | 簡單明了 | 混亂的端點,不夠 RESTful |
| 請求參數 | /api/users?version=1 | 易於實施 | 不太直觀,可以忽略。 |
| 頭部版本控制 | 接受:application/vnd.col.users.v1+json | 清潔分離 | 需要客戶意識 |
| 內容談判 | MIME 類型 | 靈活且符合 REST 規範 | 複雜的設定 |
6. 結論
在本文中,我們討論了 API 版本控制如何成為一種策略方法,在穩定性和進步性之間取得平衡,而不僅僅是一種技術安全措施。
透過 Spring Framework 7 和 Spring Boot 4,我們可以輕鬆管理 API 的演進。 Spring 現在提供了一種統一、標準化的版本控制方法,無論我們選擇基於 URI、基於標頭還是內容協商。
Spring 的 API 版本控制正在從自訂解決方案轉向原生的一流支持,使我們的 API 對客戶端友好且面向未來。
借助Spring Boot 4 的原生支持,我們現在可以採用標準化的、註解驅動的方法,從而提高一致性。
測試過的程式碼已上傳至 GitHub。