1. 程式人生 > >【Gin-API系列】實現動態路由分組(七)

【Gin-API系列】實現動態路由分組(七)

在之前的文章介紹中我們已經完成了一個API服務的全鏈路請求設計。呼叫方式可以看Test目錄的程式碼 ```golang // src/test/request_test.go func TestAPI_Request(t *testing.T) { url := "127.0.0.1:8080" ak := "A00001" sk := "SECRET-A00001" api := NewAPI(url, ak, sk) params := map[string]interface{}{ "ip": "10.1.162.18", } if result, err := api.Request("/", "GET", params); err != nil { t.Fatal(err) } else { t.Log(result) } } ``` # 重複的路由現象 截至目前我們只定義了一個路由(在`main`函式中),但現實中往往會定義多個路由實現多個API介面,而為了風格統一化(或是模組化、版本區分等原因),我們也往往會將多個路由歸為一類,這就會導致很多路由的字首相同。 所以,本文將介紹如何通過分組路由,減少路由重複程式碼並實現動態路由。 * 路由定義 ```golang func main() { ... r.GET("/", v1_sdk_search_ip.SearchIpHandlerWithGet) ... } ``` * 重複路由字首 ```bash /sdk/search_ip /sdk/search_mac /object/host /object/switch /v1/object/host /v1/object/switch /v2/object/host /v2/object/switch ``` # 分組路由 通過`Group`方法來生成一個分組,然後使用這個分組來註冊多個統一分類的路由 ```golang /* /sdk/search_ip /sdk/search_mac /object/host /object/switch */ route := gin.Default() // sdk 類別 sdkGroup := route.Group("/sdk") sdkGroup.GET("/search_mac", func(c *gin.Context) { c.String(200, "sdk search mac") }) sdkGroup.GET("/search_ip", func(c *gin.Context) { c.String(200, "sdk search ip") }) // object objGroup := route.Group("/object") objGroup.GET("/host", func(c *gin.Context) { c.String(200, "get object host") }) objGroup.POST("/switch", func(c *gin.Context) { c.String(200, "post object switch") }) ``` 如果需要對`sdk`類別的路由進行統一呼叫某個中介軟體,也可以使用`Group`的第二個引數。則不管是`search_ip`還是`search_mac`都會呼叫這個中介軟體 ```golang sdkGroup := route.Group("/sdk", func(c *gin.Context) { fmt.Println("這是sdk中介軟體") }) ``` # 巢狀分組路由 分組路由也可以繼續分組,達到巢狀分組的目的 ```golang /* /v1/object/host /v1/object/switch /v2/object/host /v2/object/switch */ v1Group := route.Group("/v1") v1ObjGroup := v1Group.Group("/object") v1ObjGroup.GET("/host", func(c *gin.Context) { c.String(200, "get object host") }) v1ObjGroup.POST("/switch", func(c *gin.Context) { c.String(200, "post object switch") }) v2Group := route.Group("/v2") v2ObjGroup := v2Group.Group("/object") v2ObjGroup.GET("/host", func(c *gin.Context) { c.String(200, "get object host") }) v2ObjGroup.POST("/switch", func(c *gin.Context) { c.String(200, "post object switch") }) ``` # Gin-IPs動態路由 有了上面路由分組和巢狀分組的基礎後,我們就可以通過自定義函式來組裝路由,實現路由的動態分發了 * 程式碼實現 ```golang type Route map[string]func(c *gin.Context) // key:uri路徑, value: 中介軟體函式 type Method int const ( GET Method = iota POST DELETE PUT ) // devPkg 對應的路由 type DevPkgGroup struct { Name configure.DevPkg Routes []map[Method]Route } // 版本對應的路由 type Group struct { Version string PkgList []DevPkgGroup } var RGroups []Group func InitRouteGroups() { RGroups = []Group{ {"v1", // RGroups[0] 表示 V1, RGroups[1] 表示 V2 []DevPkgGroup{}, }, } /*---------- 更新 V1 路由 ----------*/ // Object 路由,根據oid遍歷多個 var objectRoutes []map[Method]Route for _, oid := range configure.OidArray { // 動態新增所有的oid路由 uri, postFunc := v1_object.AllInstancesPostFunc(oid) // POST /v1/object/$oid objectRoutes = append(objectRoutes, map[Method]Route{POST: {uri: postFunc}}) uri, getFunc := v1_object.SingleInstanceGetFunc(oid) // GET /v1/object/$oid/$id objectRoutes = append(objectRoutes, map[Method]Route{GET: {uri: getFunc}}) } RGroups[0].PkgList = append(RGroups[0].PkgList, DevPkgGroup{configure.ObjectPkg, objectRoutes}) // Sdk 路由 var sdkRoutes []map[Method]Route // Sdk Get 路由 sdkGetFuncArr := []func() (string, func(c *gin.Context)){ v1_sdk.SearchIpFunc, // Get /v1/sdk/search_ip?ip='xxx' } for _, sdkGetFunc := range sdkGetFuncArr { sdkGetUri, sdkGetFunc := sdkGetFunc() sdkRoutes = append(sdkRoutes, map[Method]Route{GET: {sdkGetUri: sdkGetFunc}}) } RGroups[0].PkgList = append(RGroups[0].PkgList, DevPkgGroup{configure.SdkPkg, sdkRoutes}) } func methodMapper(group *gin.RouterGroup, method Method) func(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { if method == GET { return group.GET } if method == POST { return group.POST } if method == DELETE { return group.DELETE } if method == PUT { return group.PUT } return group.Any } // 路由解析 func AddRoute(app *gin.Engine) { cmdbGroup := app.Group("/") for _, group := range RGroups { versionGroup := cmdbGroup.Group(group.Version) for _, sdk := range group.PkgList { sdkGroup := versionGroup.Group(string(sdk.Name)) for _, mapper := range sdk.Routes { for method, route := range mapper { for uri, handler := range route { methodMapper(sdkGroup, method)(uri, handler) } } } } } } ``` * 效果展示 ```bash [GIN-debug] POST /v1/object/HOST --> Gin-IPs/src/route/v1/object.glob..func1.1 (4 handlers) [GIN-debug] GET /v1/object/HOST/:id --> Gin-IPs/src/route/v1/object.glob..func2.1 (4 handlers) [GIN-debug] POST /v1/object/SWITCH --> Gin-IPs/src/route/v1/object.glob..func1.1 (4 handlers) [GIN-debug] GET /v1/object/SWITCH/:id --> Gin-IPs/src/route/v1/object.glob..func2.1 (4 handlers) [GIN-debug] GET /v1/sdk/search_ip --> Gin-IPs/src/route/v1/sdk.glob..func1.1 (4 handlers) ``` 實現了動態路由之後我們的路由管理變得非常簡單,且很多程式碼可以重複利用。而且在此基礎上,我們還可以通過對不同的路由進行許可權控制,以實現許可權的精細管理。 ## Github 程式碼 > 請訪問 [Gin-IPs](https://github.com/AutoBingo/Gin-IPs.git) 或者搜尋 Gin-IPs