zoukankan      html  css  js  c++  java
  • 【Gin-API系列】实现动态路由分组(七)

    在之前的文章介绍中我们已经完成了一个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

  • 相关阅读:
    web前端开发最佳实践--(笔记之JavaScript最佳实践)
    web前端开发最佳实践--(笔记一)
    HTML5及CSS3--freeCodeCamp(笔记一)
    javascript系列--函数(一)
    HTML5本地存储
    分享一些好用的设计网站
    .net面试问题总结
    ife_task10--弹性盒子
    WPF--搭建一个简单的demo
    信息技术文集
  • 原文地址:https://www.cnblogs.com/lxmhhy/p/13614097.html
Copyright © 2011-2022 走看看