路由

路由系统的用于接收请求,并将请求转发给注册的中间件或请求处理器来处理。核心功能如下:

  • 路由系统可根据请求方法,请求路径,和路径参数来识别转发。
  • 可设置一个或多个中间件用于在请求处理器前后,处理特殊的事件。
  • 可以分组设置,将一个或多个中间件作用在一组多个路由上。

基本语法

我们需要先构建路由对象,也就是 gin.Engine 路由对象,之后在路由对象上注册请求路径对应的处理器(包括中间件),最后通过路由对象的 router.Run 方法启动监听。示例代码如下:

r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
	c.JSON(200, gin.H{
		"message": "pong",
	})
})
r.Run() // 监听并在 0.0.0.0:8080 上启动服务
1
2
3
4
5
6
7

其中:

  • line 1,初始化了默认的路由。
  • line 2-6,针对于 GET 方式的 /ping 注册了请求处理器,就是后边的匿名函数。
  • line 7,运行监听。

代码中,line 2-6 会反复的出现,我们通常需要注册多个请求处理器完成一个完整的项目。

一个建议是将路由的定义放在独立的文件中完成,例如在 /router/api.go 中完成:

// file /router/api.go
func APIRouter() *gin.Engine {
	r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    // 更多的注册
    // r.POST()
    // r.DELETE()
    // r.PUT()
    return r
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

之后,在 main.main() 中完成调用初始化函数,并启用监听:

// file /main.go
func main() {
	// 初始化路由
	r := router.APIRouter()
	// 启动服务
	r.Run()
}
1
2
3
4
5
6
7

请求方法

快捷方法

路由系统支持任意方式的请求,如下的方法用来提供对应方法来接收请求:

func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes
func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes
func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes
func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes
func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes
1
2
3
4
5
6
7

调用方法与上面的例子中一致。

通用语法

若需要接收其他的请求方式,可以使用通用的方法 Handle 来注册:

 func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes
1

其中 httpMethod 就是请求方法字符串,例如 TRACE,CONNECT 等。

其实,类似 DELETE(),GET(),POST() 这些方法就是调用了 Handle 的快捷语法。

任意方法

若需要监听同一个请求路径的多个任意方法,可以使用 Any(),这样就可以同时监听任意的请求方法了 :

 func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes
1

处理器

handler 处理器,用于处理 HTTP 请求。在典型的 MVC 架构中也被叫做控制器的动作(action in controller)。是一个满足如下前面的函数:

type HandlerFunc func(*Context)
1

形式非常简单,就是一个可以接收 *Context 类型(*gin.Context)参数的函数即可。

处理器分为两类,中间件和请求处理器(业务逻辑处理器)。在 router.GET() 类的的方法中,最后一个 handler 就是请求处理器,除此之外前边的都是中间件,必须要有一个 handler 才可以。

处理器被调用时,会接收一个 *Context 参数,是请求上下文,我们用于获取请求和操作响应。请参考请求上下文,或请求,或响应章节,获得更多内容。

路由参数

指的是在路由路径中定义的参数,例如请求 URI 是 /user/42,42 作为用户的 ID,那么 42 就是路由参数。注意路由参数不是查询字符串,例如 /user?ID=42, 这个 ID=42,也就是问号后边的才是查询字符串。

使用路由参数的好处是将动态 URL 变为 静态 URL,因为请求客户端会认为路由参数不是变化的数据,因此被视为静态 URL。

若需要定义带有路由参数的路径,需要使用 :param*param 的语法在路径中。

必选参数

使用 :param 的语法完成必选参数,例如 /user/:ID,即可匹配 /user/42 这个 URI,但不能匹配 /user/user/ 这个 URI,例如:

router.GET("/user/:ID", func(c *gin.Context) {
	ID := c.Param("ID")
})
1
2
3

c.Param(key string) string 方法返回的是字符串类型参数,注意使用时的类型处理。

可选参数

使用 *param 的语法完成可选参数的定义,例如 /user/*ID 可以匹配 /user/42/user/user 的 URI,例如:

router.GET("/user/*ID", func(c *gin.Context) {
	ID := c.Param("ID")
})
1
2
3

若没有匹配到,则 ID 为空字符串。

获取参数

使用 gin.Context 对象的 c.Param("param") 来获取参数值,参见上面的示例。

路由分组

路由分组用于将多个路由进行统一的处理,例如统一的前缀,统一的中间件等。例如,在做需要认证的业务逻辑时,大量的路由需要经过认证校验后才能交由请求处理器来完成业务逻辑,此时就可以将大量的路由定义在一个组中,集中的设置这个用于认证校验的中间件。

创建分组

使用函数 router.Group() 完成分组的创建。创建时可以提供路径前缀和公用中间件,router.Group() 函数签名如下:

func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup
1

调动该函数后,会形成一个分组路由对象,组内的路由需要使用该对象完成处理器的注册,例如:

// 简单的路由组: 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)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

上面的代码创建了两个路由分组,前缀分别是 v1 和 v2 。本例中,并没有设置任何的中间件,这是可行的。

注意,v1 组的路由的注册方案,都是由 v1. 调用。

语法上,我们习惯(也是官方建议)将一组路由放在一个代码块中,在结构上保持独立。但这个代码块不是必要的。

中间件

中间件 middleware,也是一种处理器。主要用于在多个业务逻辑中间重用代码,例如认证校验,日志处理等。

中间件需要附加在路由上,使用 router.User() 方法。关于中间件的介绍,请参考中间件章节。

我们在使用 gin.Default() 初始化路由对象时,会随之附加两个中间件 LoggerRecovery ,用于完成日志和恢复的相关处理,参考的 gin.Default()代码如下:

func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}
1
2
3
4
5
6

其中,line 4 在注册中间件。

若必须要使用任何中间件,可以使用函数 gin.New() 来创建空白的路由,例如:

router := gin.New()
1

此时的路由对象,不会自动附加任何的中间件。

启动监听

函数 router.Run() 用于启动 HTTP 监听,函数接收监听地址作为参数,默认地址为 :8080。成功则守护执行,失败则返回错误。

常用方法索引

gin.New(),创建空路由

函数签名:

func New() *Engine
1

创建空白路由,不带有任何中间件。

参数:无

返回值:*gin.Engine

gin.Default(),创建默认路由

函数签名:

func Default() *Engine
1

创建带有默认中间件的路由。

参数:无

返回值:*gin.Engine

默认的中间为:

  • Logger,日志

  • Recovery,恢复

支持不同的请求方法的路由解析。通用的方法是 router.Handle()

router.Handle(),注册请求处理器

函数签名:

func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes
1

为给定的 path 和 method 注册处请求处理器和中间件。

最后的处理器是请求处理器,其他的处理器应是可以在不同路由间共享的中间件。

参数:

  • httpMethod string,请求方法
  • relativePath string,相对路径
  • handlers ...HandlerFunc,处理器函数

返回值:

  • IRoutes,接口

除了该通用的注册函数,常用的请求方法有快捷语法,参考下列函数:

router.GET(),注册 GET 方法请求处理器

函数签名:

func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes
1

router.POST(),注册 POST 方法请求处理器

函数签名:

func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes
1

router.PUT(),注册 PUT 方法请求处理器

函数签名:

func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes
1

router.DELETE(),注册 DELETE 方法请求处理器

函数签名:

func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes
1

router.PATCH(),注册 GET 方法请求处理器

函数签名:

func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes
1

router.HEAD(),注册 GET 方法请求处理器

函数签名:

func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes
1

router.OPTIONS(),注册 OPTIONS 方法请求处理器

函数签名:

func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes
1

router.ANY(),注册任意方法请求处理器

函数签名:

func (group *RouterGroup) ANY(relativePath string, handlers ...HandlerFunc) IRoutes
1

router.Run(),运行监听

函数签名:

func (engine *Engine) Run(addr ...string) (err error)
1

启动监听。

参数:

  • addr,监听的地址,默认为 ":8080"