Gin 中许多类型都可以认为它是一个路由(IRoutes)类型,这包括了 RouterGroup、Engine等。并且 IRoutes 接口中定义的方法都会有一个 IRoutes 类型的返回值,使得 Gin Routers 可以支持链式调用,让代码更加简洁。
路由分组
RouterGroup 是一种路由组对象(Engine 继承自 RouterGroup,所以也可以认为是一个路由组)。通过 Group() 方法可以创建一个新的 RouterGroup。Group() 方法的定义如下:
1func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
2 return &RouterGroup{
3 Handlers: group.combineHandlers(handlers),
4 basePath: group.calculateAbsolutePath(relativePath),
5 engine: group.engine,
6 }
7}
relativePath:路由组的相对路径。与handle()方法 相同,relativePath同样需要使用group.calculateAbsolutePath()来计算出绝对路径。handlers:路由组的处理方法,一般是将其作为中间处理函数(中间件)来定义。
RouterGroup 类型的定义如下:
1type RouterGroup struct {
2 Handlers HandlersChain
3 basePath string
4 engine *Engine
5 root bool
6}
其中:
- 使用
root和engine字段来标记当前路由组是否为根路由,以及根路由的地址(Engine可以认为是当前 Gin 程序中所有路由组的根路由组); Handlers记录了当前路由组中所需要的一系列路由处理函数(其中也包括了它上层路由的处理函数);basePath指明了当前路由组的绝对路径。
路由组的使用方式如下所示:
1// 创建路由组
2user := r.Group("/user", middlewares.UserMiddleware)
3{
4 user.GET("", func(context *gin.Context) {
5 context.String(http.StatusOK, "User get\n")
6 })
7 user.POST("", func(context *gin.Context) {
8 context.String(http.StatusOK, "User post\n")
9 })
10 user.PUT("", func(context *gin.Context) {
11 context.String(http.StatusOK, "User put\n")
12 })
13 user.DELETE("", func(context *gin.Context) {
14 context.String(http.StatusOK, "User delete\n")
15 })
16}
对于任何一个 RouterGroup 来说,可以继续使用 Group() 方法创建路由,即使该 RouterGroup 也是由其它 RouterGroup 创建的。
Routers 包
一般在项目中,会专门创建一个 routers 包来存放项目中路由配置的相关代码。
将一系列相关的路由抽取为理由组后,可以专门为其在 routers 包下创建一个文件来存放这一系列路由的配置。例如可以将 user 路由中的内容配置在 user.go 中:
1func UserRoutersInit(engin *gin.Engine) {
2 user := engin.Group("/user", middlewares.UserMiddleware)
3
4 user.GET("", func(context *gin.Context) {
5 context.String(http.StatusOK, "User get\n")
6 })
7 user.POST("", func(context *gin.Context) {
8 context.String(http.StatusOK, "User post\n")
9 })
10 user.PUT("", func(context *gin.Context) {
11 context.String(http.StatusOK, "User put\n")
12 })
13 user.DELETE("", func(context *gin.Context) {
14 context.String(http.StatusOK, "User delete\n")
15 })
16
17 // 相对路径传入空串,表示请求路径与路由组的路径相同
18 // 需要注意,不能使用 "/" 来表示 “当前请求路径与路由组的路径相同” 这一含义
19}
在 routers 包中可以创建一个 routers.go 来配置根路由、设置全局中间件以及初始化其它路由:
1func Router() (engine *gin.Engine) {
2 // 创建根路由
3 engine = gin.Default()
4
5 // 初始化其它子路由
6 UserRoutersInit(engine)
7 // ...
8
9 return
10}
根路由的创建除了使用 gin.Default() 外,还可以使用 gin.New()。gin.Default() 默认使用了 gin.Logger() 和 gin.Recovery()。使用 gin.New() 来创建路由,需要自己手动配置 Logger 和 Recovery 中间件:
1engine = gin.New()
2engine.Use(gin.Logger(), gin.Recovery())
gin.Logger():日志。gin.Recovery():错误管理,它会recover()任何panic(),并且返回给客户端500响应码。
Controllers 包
除了路由,路由处理器也可以定义在专门的 controllers 包中。例如,user 路由,首先在 controllers 包中创建 user.go:
1// userController 类型
2type userController struct{}
3// UserController 示例
4// 通过内部类型,以及定义外部实例,模拟一个简单的单例模式
5var UserController userController
6
7func (userController) GetUser(ctx *gin.Context) {
8 context.String(http.StatusOK, "User get\n")
9}
10
11func (userController) AddUser(ctx *gin.Context) {
12 context.String(http.StatusOK, "User post\n")
13}
14
15func (userController) UpdateUser(ctx *gin.Context) {
16 context.String(http.StatusOK, "User put\n")
17}
18
19func (userController) DeleteUser(ctx *gin.Context) {
20 context.String(http.StatusOK, "User delete\n")
21}
接着,修改 routers/user.go:
1user.GET("", controllers.UserController.GetUser)
2user.POST("", controllers.UserController.AddUser)
3user.PUT("", controllers.UserController.UpdateUser)
4user.DELETE("", controllers.UserController.DeleteUser)
评论