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

【Gin-API系列】实现路由分组(七)的更多相关文章

  1. Gin的中间件和路由分组

    什么是分组 对router创建Group(就是分组), 对同一分组会拥有同一前缀和同一中间件 写法 eg: r := gin.Default() v1 := r.Group("/v1&quo ...

  2. Gin框架系列02:路由与参数

    回顾 上一节我们用Gin框架快速搭建了一个GET请求的接口,今天来学习路由和参数的获取. 请求动词 熟悉RESTful的同学应该知道,RESTful是网络应用程序的一种设计风格和开发方式,每一个URI ...

  3. 七天接手react项目 系列 —— react 路由

    其他章节请看: 七天接手react项目 系列 react 路由 本篇首先讲解路由原理,接着以一个基础路由示例为起点讲述路由最基础的知识,然后讲解嵌套路由.路由传参,最后讲解路由组件和一般组件的区别,以 ...

  4. [Web API] Web API 2 深入系列(1) 路由

    目录 ASP.NET 路由 注册路由 动态映射HttpHandler WebAPI 路由 注册路由 调用GetRouteData 2个路由系统衔接 GlobalConfiguration Hosted ...

  5. Gin 框架 - 安装和路由配置

    目录 概述 Gin 安装 路由配置 推荐阅读 概述 看下 Gin 框架的官方介绍: Gin 是一个用 Go (Golang) 编写的 web 框架. 它是一个类似于 martini 但拥有更好性能的 ...

  6. C#进阶系列——WebApi 路由机制剖析:你准备好了吗?

    前言:从MVC到WebApi,路由机制一直是伴随着这些技术的一个重要组成部分. 它可以很简单:如果你仅仅只需要会用一些简单的路由,如/Home/Index,那么你只需要配置一个默认路由就能简单搞定: ...

  7. ASP.NET Web API系列教程目录

    ASP.NET Web API系列教程目录 Introduction:What's This New Web API?引子:新的Web API是什么? Chapter 1: Getting Start ...

  8. flask开发restful api系列(8)-再谈项目结构

    上一章,我们讲到,怎么用蓝图建造一个好的项目,今天我们继续深入.上一章中,我们所有的接口都写在view.py中,如果几十个,还稍微好管理一点,假如上百个,上千个,怎么找?所有接口堆在一起就显得杂乱无章 ...

  9. 【ASP.NET Web API教程】4.1 ASP.NET Web API中的路由

    原文:[ASP.NET Web API教程]4.1 ASP.NET Web API中的路由 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的内容. ...

随机推荐

  1. 33-关键字:interface

    interface:接口 1.使用说明: * 1.接口使用interface来定义 * 2.Java中,接口和类是并列的两个结构 * 3.如何定义接口:定义接口中的成员 * 3.1 JDK7及以前:只 ...

  2. Linux集群配置离线ntp时间同步服务

    集群中时间不同步有可能会让大数据的应用程序运行混乱,造成不可预知的问题,比如Hbase.mongodb副本集等,Hbase当时间差别过大时就会挂掉,mongodb如果副本时间过快,会出现时间栈帧溢出提 ...

  3. 用了Dapper之后通篇还是SqlConnection,真的看不下去了

    一:背景 1. 讲故事 前几天看公司一个新项目的底层使用了dapper,大家都知道dapper是一个非常强大的半自动化orm,帮程序员解决了繁琐的mapping问题,用起来非常爽,但我还是遇到了一件非 ...

  4. ES6 面向对象笔记

    JS面向对象两大编程思想 面向过程 面向对象 面向过程编程POP         面向过程就是分析出问题的需要步骤,然后用函数一步一步的实现,使用的时候一个一个调用就可以了 面向对象编程OOP     ...

  5. Java集合最全解析,学集合,看这篇就够用了!!!

    在看集合类之前, 我们要先明白一下概念: 1.数据结构 (1):线性表 [1]:顺序存储结构(也叫顺序表) 一个线性表是n个具有相同特性的数据元素的有限序列.数据元素是一个抽象的符号,其具体含义在不同 ...

  6. 怎么用 Solon 开发基于 undertow jsp tld 的项目?

    Solon 开发 jsp 还是简单的,可以有 jetty 启动器 或者 undertow 启动器.此文用 undertow + jsp + tld 这个套路搞一把: 一. 开始Meven配置走起 用s ...

  7. Catalina 默认使用zsh了,你可习惯

    zsh 成为默认 shell 淘汰掉我的旧MBP换新后,欢天喜地打开Terminal,感觉有点不对,提示符什么时候变成了 %. 查询了一些资料发现,原来在2019年WWDC期间,苹果推出了macOS ...

  8. Revit二开---Schemachema扩展数据

    一.什么是Schema        Schema是Revit扩展数据的技术关键词,revit到这里,需要对Revit二开基础有一定了解. 二.Schema架构        建立revit扩展数据第 ...

  9. C#LeetCode刷题之#628-三个数的最大乘积( Maximum Product of Three Numbers)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3726 访问. 给定一个整型数组,在数组中找出由三个数组成的最大乘 ...

  10. Vue 使用$createElement自定义文本

    有时候弹窗或者一些特殊的区域需要自定义一些html元素来显示(snabbdom) const h = this.$createElement this.$msgbox({ title: '提示', m ...