Go 項目實戰 - API 路由的分模塊管理

隨着項目開發的迭代,我們寫的接口往往會越來越多,如果都把 API 的路由寫到一個文件裏,那麼整個路由文件就會變得又亂又長,所以我們最好在項目開始階段就給路由的分模塊管理做好規劃。

今天這個文章給大家介紹一下 Web 項目 API 路由的分模塊管理,我們的項目使用的是 Gin 框架,但基本上所有的 Web 框架都能按照這個方式來分模塊管理 API 接口的路由。

一些路由管理混亂的例子

首先,我先給大家看一個曾經維護過的項目的路由文件 router.go, 這個項目用的也是 Gin 框架,整個文件裏 500 多行全是 API 接口的路由。


你說這麼寫不好維護吧,全項目的路由都在這裏不用其他地方找,按能用就行的標準,確實是能用。

而且 Gin 的官方文檔裏在路由這塊的例子確實也是這麼寫的。

// Gin 官方文檔示例
func main() {
 router := gin.Default()

 // 簡單的路由組: v1
 v1 := router.Group("/v1")
 {
  v1.POST("/login", loginEndpoint)
  v1.POST("/submit", submitEndpoint)
  v1.POST("/read", readEndpoint)
 }

 // 簡單的路由組: v2
 v2 := router.Group("/v2")
 {
  v2.POST("/login", loginEndpoint)
  v2.POST("/submit", submitEndpoint)
  v2.POST("/read", readEndpoint)
 }

 router.Run(":8080")
}

隨着項目開發的迭代,我們寫的接口往往會越來越多,如果還按上面這樣把 API 的路由寫到一個文件裏,那麼整個路由文件就會變得像上面那個例子一樣,變得又亂又長。

項目中怎麼規劃和管理路由

首先根據我們上一節 「Go 項目怎麼做好分層架構和目錄規劃」中設計的項目目錄結構,在 API 處理器對應的 api 目錄下的 controler 和 router 子目錄中分別存放每個模塊對應的 Api handler 和 router 文件。

舉例來說,假設我們項目中想在有用戶和訂單兩個模塊,那麼此時項目的 api/controller 和 api/router 中應該分別有倆個文件與業務模塊對應。

.
|---api # API 處理器模塊
|     |---controller  # 控制器
|     |   |---order.go  # 訂單模塊的 Api Handler
|     |   |---user.go  # 用戶模塊的 Api Handler
|     |---router  # 路由
|     |   |---order.go  # 訂單模塊的路由文件
|     |   |---router.go  # 負責路由初始化和註冊各模塊路由的總文件
|     |   |---user.go  # 用戶模塊的路由文件

在路由目錄中 router.go 負責路由初始化和註冊各模塊路由的總文件,此外一些要全局應用的中間件也會在這裏設置,比如像下面這樣。

而進入到每個模塊的路由文件中,首先其路由組設置的路由前綴要跟模塊名保持統一,另外還可以根據該模塊中接口的統一特徵在路由組上應用中間件。

比如是訂單模塊的接口,那麼路由組的前綴可以設置成 "/order/" 這樣所有訂單相關的接口都在這個路徑下,因爲用戶只能看自己的訂單,所以所有訂單相關的接口都需要用戶認證後才能訪問。我們可以在路由組上應用用戶認證中間件,爲組內的所有接口增加這項限制,比如像下面這樣。

最後多提一點,如果業務模塊裏的接口太多,像 controller/order.go 這樣,單個文件不好組織整個模塊的 API handler 的時候也可以把其升級爲目錄,變成下面這種結構。

.
|---api # API 處理器模塊
|     |---controller  # 控制器
|     |   |---order # 訂單模塊的 Api Handler
|     |   |   |---xxx.go
|     |   |   |---yyy.go
|     |---router  # 路由
......

好了,介紹完 Web 項目管理路由的大概思路後,我帶大家一起看下,怎麼用這個思路在 Gin 項目中分模塊管理

用 Gin 實現路由的分模塊管理

分模塊首先就是按照 URI 的目錄或者叫路由組進行管理,首先我們在項目的 api/router 目錄下定義一個 router.go 文件,它負責路由初始化和註冊各模塊的路由。

在其中增加如下代碼:

func RegisterRoutes(engine *gin.Engine) {
 // use global middlewares
 engine.Use(middleware.StartTrace(), middleware.LogAccess(), middleware.GinPanicRecovery())
 routeGroup := engine.Group("")
 registerBuildingRoutes(routeGroup)
}

在這裏我們先把所有全局中間件應用上,Gin 框架的路由組是靠 gin.Group 來維護的,我們先在全局的 router 方法中通過 engine.Group("") 拿到一個不帶任何路由前綴的 gin.Group 作爲頂級路由組。

之後再把它傳遞給每個子模塊的路由註冊方法,在這個頂級路由組的基礎上再去生成各個路由模塊的路由組對象,用來註冊它們各自的路由。

我們先把之前搭建框架時寫的那些測試方法的路由都放在 api/router/building.go 文件中,所有路由都以 "/building/" 作爲前綴。

// 存放一些項目搭建過程中驗證效果用的接口的路由

func registerBuildingRoutes(rg *gin.RouterGroup) {
 // 這個路由組中的路由都以 /building 開頭
 g := rg.Group("/building/")
 // 測試 Ping
 g.GET("ping", controller.TestPing)
 // 測試日誌文件的讀取
 g.GET("config-read", controller.TestConfigRead)
 // 測試日誌門面Logger的使用
 g.GET("logger-test", controller.TestLogger)
 // 測試服務的訪問日誌
 g.POST("access-log-test", controller.TestAccessLog)
 // 測試服務的崩潰日誌
 g.GET("panic-log-test", controller.TestPanicLog)
 // 測試項目自定義的AppError 打印錯誤鏈條和錯誤發生位置
 g.GET("customized-error-test", controller.TestAppError)
 // 測試統一響應--返回對象數據
 g.GET("response-obj", controller.TestResponseObj)
 // 測試統一響應--返回列表和分頁
 g.GET("response-list", controller.TestResponseList)
 // 測試統一響應--返回錯誤
 g.GET("response-error", controller.TestResponseError)
}

相應的路由對應的 API Handler 也需要從 main.go 中挪到 controller 包中, 我們在 api/controller 中新建 building.go 用來存放搭建框架過程中編寫的那些測試接口的 Handler 方法。

把已有的 Controller 挪到對應的文件後,可以隨機抽查幾個看看看到這些接口是否都還能正常訪問,接下來再觀察下我們請求接口時產生的應用日誌。

應用日誌仍然是能正常的把請求的訪問日誌和錯誤響應日誌都給記錄下來,證明代碼改動沒問題。

路由分模塊管理的規則

上面我演示了爲了做路由分模塊管理在項目中做的那些基礎工作,未來進入需求開發階段我們只要按照這個規則分模塊去管理路由就行啦。

後面我們在項目開發時,API 的路由管理也遵循這個原則:

本節對應的代碼版本爲 c6,訂閱後加入課程的 GitHub 項目後訪問 https://github.com/go-study-lab/go-mall/compare/c5...c6 可以直接查看本章節對應的代碼更新。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/3X-JiLhKPCqtdB8xUVIJEA