在之前的文章介绍中我们已经完成了一个API服务的全链路请求设计。调用方式可以看Test目录的代码

// 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接口,而为了风格统一化(或是模块化、版本区分等原因),我们也往往会将多个路由归为一类,这就会导致很多路由的前缀相同。

所以,本文将介绍如何通过分组路由,减少路由重复代码并实现动态路由。

  • 路由定义
func main() {
...
r.GET("/", v1_sdk_search_ip.SearchIpHandlerWithGet)
...
}
  • 重复路由前缀
/sdk/search_ip
/sdk/search_mac /object/host
/object/switch /v1/object/host
/v1/object/switch /v2/object/host
/v2/object/switch

分组路由

通过Group方法来生成一个分组,然后使用这个分组来注册多个统一分类的路由

/*
/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都会调用这个中间件

sdkGroup := route.Group("/sdk", func(c *gin.Context) {
fmt.Println("这是sdk中间件")
})

嵌套分组路由

分组路由也可以继续分组,达到嵌套分组的目的

/*
/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动态路由

有了上面路由分组和嵌套分组的基础后,我们就可以通过自定义函数来组装路由,实现路由的动态分发了

  • 代码实现
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)
}
}
}
}
}
}
  • 效果展示
[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 或者搜索 Gin-IPs

最新文章

  1. HttpContext.Current.Session.SessionID相关问题及备忘
  2. MVC 本地运行可以发布到IIS 报Sorry, an error occurred while processing your request.解决方案
  3. jQuery的常见操作
  4. ofo走出校园观察:市场定位导致产品错位?
  5. sqoop与mysql之间中文乱码
  6. IE9 不F12打开控制台,代码不执行。打开后正常
  7. 三星嵌入式开发平台 三星Cortex-A9 4412 POP与SCP对比
  8. [SAP ABAP开发技术总结]报表事件
  9. Dynamic Prompt Table for Record Field on PeopleSoft Page
  10. LeetCode 75
  11. zoj 3841 Cards
  12. 通过 Xftp5 管理 centOS 7 文件
  13. Shell中的算术运算(译)
  14. final 、finally 和 finalize()的区别
  15. Mysql中一级缓存二级缓存区别
  16. node.js 连接数据库
  17. <玩转Django2.0>读书笔记:邮件和分页
  18. [ionic3.x开发记录]ios下页面过渡效果不出现的小坑
  19. [LeetCode] 45. Jump Game II_ Hard tag: Dynamic Programming
  20. MySQL忘记root密码的解决办法

热门文章

  1. Tarjan 做题总结
  2. 2020牛客暑期多校训练营(第八场)K-Kabaleo Lite题解
  3. 学会这些Python美图技巧,就等着女朋友夸你吧
  4. 前端自适应样式(reset.css)
  5. python3.x与2.x中print输出不换行
  6. C#LeetCode刷题之#589-N叉树的前序遍历(N-ary Tree Preorder Traversal)
  7. centos环境 使用kubeadm快速安装k8s集群v1.16.2
  8. LeetCode 646 最长数对链详解
  9. golang 的 string包
  10. 基础类库积累--ExeclHelper类