对控制器的分组与继承
1 )设计项目目录结构
tree
yourGinProject/ ······························· 根目录
├── go.mod ·································· go mod 文件
├── go.sum ·································· go sum 文件
├── main.go ································· main 文件
└── tpls ····································· html模板目录
│ └── web
│ │ └── index.html
├── routers ·································· 路由目录
│ ├── webRouters.go
│ ├── apiRouters.go
│ └── adminRouters.go
├── controllers ······························ 控制器目录
│ ├── web
│ │ └── webCtrl.go
│ ├── api
│ │ └── apiCtrl.go
│ └── admin
│ │ ├── base.go
│ │ ├── indexCtrl.go
│ │ └── userCtrl.go
2 )主程序 main.go
go
package main
import (
"gin-demo/routers" //gin-demo 是 go mod init 初始化的工程,下同
"github.com/gin-gonic/gin"
)
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
//加载模板 放在配置路由前面
r.LoadHTMLGlob("tpls/**/*")
routers.WebRoutersInit(r)
routers.ApiRoutersInit(r)
routers.AdminRoutersInit(r)
r.Run()
}
3 ) HTML模板目录配置
tpls/web/index.html
html
{{ define "web/index.html" }}
<h1>web index 页面</h1>
{{.msg}}
{{ end }}
4 ) routers 配置
4.1 webRouters.go
go
package routers
import (
"gin-demo/controllers/web"
"github.com/gin-gonic/gin"
)
func WebRoutersInit(r *gin.Engine) {
webRouters := r.Group("/")
{
webRouters.GET("/", web.WebCtrl{}.Index)
}
}
4.2 apiRouters.go
go
package routers
import (
"gin-demo/controllers/api"
"github.com/gin-gonic/gin"
)
func ApiRoutersInit(r *gin.Engine) {
apiRouters := r.Group("/api")
{
apiRouters.GET("/", api.ApiCtrl{}.Index)
apiRouters.GET("/user", api.ApiCtrl{}.User)
}
}
4.2 adminRouters.go
go
package routers
import (
"gin-demo/controllers/admin"
"github.com/gin-gonic/gin"
)
func AdminRoutersInit(r *gin.Engine) {
adminRouters := r.Group("/admin")
{
adminRouters.GET("/", admin.IndexCtrl{}.Index)
adminRouters.GET("/user", admin.UserCtrl{}.Index)
adminRouters.GET("/user/success", admin.UserCtrl{}.Success)
adminRouters.GET("/user/error", admin.UserCtrl{}.Error)
}
}
5 ) controller 配置
5.1 web/webCtrl.go
go
package web
import (
"net/http"
"github.com/gin-gonic/gin"
)
type WebCtrl struct{}
func (con WebCtrl) Index(c *gin.Context) {
c.HTML(http.StatusOK, "web/index.html", gin.H{
"msg": "我是一个msg",
})
}
5.2 api/apiCtrl.go
go
package api
import (
"github.com/gin-gonic/gin"
"net/http"
)
type ApiCtrl struct{}
func (con ApiCtrl) Index(c *gin.Context) {
c.String(http.StatusOK, "api接口总承")
}
func (con ApiCtrl) User(c *gin.Context) {
c.String(http.StatusOK, "这是一个 user 接口")
}
5.3 admin/indexCtrl.go
go
package admin
import (
"github.com/gin-gonic/gin"
"net/http"
)
type IndexCtrl struct {}
func (con IndexCtrl) Index(c *gin.Context) {
c.String(http.StatusOK, "admin 页面")
}
5.4 admin/baseCtrl.go
go
package admin
import (
"github.com/gin-gonic/gin"
"net/http"
)
type BaseCtrl struct{}
func (con BaseCtrl) success(c *gin.Context) {
c.String(http.StatusOK, "成功")
}
func (con BaseCtrl) error(c *gin.Context) {
c.String(http.StatusOK, "失败")
}
5.4 admin/userCtrl.go
go
package admin
import (
"github.com/gin-gonic/gin"
"net/http"
)
type UserCtrl struct {
BaseCtrl
}
func (con UserCtrl) Index(c *gin.Context) {
c.String(http.StatusOK, "user 页面")
}
func (con UserCtrl) Success(c *gin.Context) {
con.success(c)
}
func (con UserCtrl) Error(c *gin.Context) {
con.error(c)
}
以上就是对控制器的一般文件拆分和继承关系的调用示例,验证如下
/
访问首页
/api
/api/user
/admin
/admin/user
/admin/user/success
/admin/user/error
以上均可正常访问,这样就可以最快完成一个项目的拆分
中间件的处理
1 ) 基础用法, 单一中间件
go
package main
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
"net/http"
)
func initMiddleware(c *gin.Context) {
// 记录开始时间
start := time.Now().UnixNano()
// 调用该请求的剩余处理程序
c.Next()
// 记录结束时间
end := time.Now().UnixNano()
// 输出当前渲染时间差
fmt.Println("时间:", end - start)
}
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
r.GET("/", initMiddleware, func(c *gin.Context) {
c.String(http.StatusOK, "首页")
})
r.GET("/news", initMiddleware, func(c *gin.Context) {
c.String(http.StatusOK, "新闻页面")
})
r.Run()
}
- 中间件就是匹配路由前和匹配路由完成后执行的一系列操作
- 中间件必须是一个 gin.HandlerFunc 类型
2 )多个路由中间件
go
package main
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
"net/http"
)
func initMiddleware(c *gin.Context) {
fmt.Println("第1个中间件开始")
// 记录开始时间
start := time.Now().UnixNano()
// 调用该请求的剩余处理程序
c.Next()
// 记录结束时间
end := time.Now().UnixNano()
// 输出当前渲染时间差
fmt.Println("第1个中间件结束,并统计其处理时间:", end - start)
}
func initMiddleware2(c *gin.Context) {
fmt.Println("第2个中间件开始")
c.Next()
// 终止调用该请求的剩余处理程序
// c.Abort() // 注意,Next 和 Abort 只能二选一,可以控制在某些情况下,终止中间件
fmt.Println("第2个中间件结束")
}
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
r.GET("/", initMiddleware, initMiddleware2, func(c *gin.Context) {
c.String(http.StatusOK, "首页")
})
r.GET("/news", initMiddleware, initMiddleware2, func(c *gin.Context) {
c.String(http.StatusOK, "新闻页面")
})
r.Run()
}
-
上述示例中,有两个中间件,就是 initMiddleware, initMiddleware2
-
访问路由时的输出顺序
第1个中间件开始 第2个中间件开始 第2个中间件结束 第1个中间件结束,并统计其处理时间: 21000
-
这种就是洋葱模型,基本上所有中间件都符合这一模型
-
配置路由的时候可以传递多个 func 回调函数
-
最后一个 func 回调函数前面触发的方法都可以称为中间件
-
中间件里面加上 ctx.Next()可以让我们在路由匹配完成后执行一些操作
-
如果想要终止中间件操作可以通过判断,添加 ctx.Abort() 来终止接下来的操作
3 )全局中间件
go
package main
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
"net/http"
)
func initMiddleware(c *gin.Context) {
fmt.Println("第1个中间件开始")
// 记录开始时间
start := time.Now().UnixNano()
// 调用该请求的剩余处理程序
c.Next()
// 记录结束时间
end := time.Now().UnixNano()
// 输出当前渲染时间差
fmt.Println("第1个中间件结束,并统计其处理时间:", end - start)
}
func initMiddleware2(c *gin.Context) {
fmt.Println("第2个中间件开始")
c.Next()
fmt.Println("第2个中间件结束")
}
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
// 全局中间件
r.Use(initMiddleware, initMiddleware2)
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "首页")
})
r.GET("/news", func(c *gin.Context) {
c.String(http.StatusOK, "新闻页面")
})
r.Run()
}
- 这种属于全局配置的中间件,不用在每个路由中书写,进行全局use
- 这种写法和第2种效果一致
4 )中间件的拆分
tree
yourGinProject/ ······························· 根目录
├── go.mod ·································· go mod 文件
├── go.sum ·································· go sum 文件
├── main.go ································· main 文件
└── tpls ····································· html模板目录
│ └── web
│ │ └── index.html
├── routers ·································· 路由目录
│ ├── webRouters.go
│ ├── apiRouters.go
│ └── adminRouters.go
├── controllers ······························ 控制器目录
│ ├── web
│ │ └── webCtrl.go
│ ├── api
│ │ └── apiCtrl.go
│ └── admin
│ │ ├── base.go
│ │ ├── indexCtrl.go
│ │ └── userCtrl.go
├── middlewares ······························ 中间件目录
│ └── init.go
这里使用最顶层控制器拆分时用的结构
这里 middlewares/init.go
go
package middlewares
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
)
func InitMiddleware(c *gin.Context) {
//判断用户是否登录
fmt.Println("当前时间:", time.Now())
fmt.Println("当前URL:", c.Request.URL)
c.Set("username", "Wang") // 在请求上下文中设置值,后续的处理函数能够取到该值
// 定义一个 goroutine 统计日志
// 当在中间件或 handler 中启动新的 goroutine 时
// 不能使用原始的上下文(c *gin.Context), 必须使用其只读副本(c.Copy())
cCp := c.Copy()
go func() {
time.Sleep(2 * time.Second)
fmt.Println("Done! in path " + cCp.Request.URL.Path)
}()
}
改造 routers/adminRouters.go 文件
go
package routers
import (
"gin-demo/controllers/admin"
"github.com/gin-gonic/gin"
"gin-demo/middlewares" // 引入
)
func AdminRoutersInit(r *gin.Engine) {
adminRouters := r.Group("/admin", middlewares.InitMiddleware) // 注意这里
{
adminRouters.GET("/", admin.IndexCtrl{}.Index)
adminRouters.GET("/user", admin.UserCtrl{}.Index)
adminRouters.GET("/user/success", admin.UserCtrl{}.Success)
adminRouters.GET("/user/error", admin.UserCtrl{}.Error)
}
}
在 /admin
及子路由 被访问时都会经过这个中间件
这里用了一个 goroutine 做数据统计,下面在 admin.userCtrl 中获取中间件中配置的值
改造 controllers/admin/userCtrl.go 文件
go
package admin
import (
"net/http"
"github.com/gin-gonic/gin"
)
type UserCtrl struct {
BaseCtrl
}
func (con UserCtrl) Index(c *gin.Context) {
username, _ := c.Get("username") // 这里从中间件中读取数据
c.String(http.StatusOK, "user 页面: %v", username) // 响应出去
}
func (con UserCtrl) Success(c *gin.Context) {
con.success(c)
}
func (con UserCtrl) Error(c *gin.Context) {
con.error(c)
}
这样就可以获取到中间件中读取的数据了
注意事项
- gin 默认中间件
- gin.Default()默认使用了 Logger 和 Recovery 中间件,其中:
- Logger 中间件将日志写入 gin.DefaultWriter,即使配置了 GIN_MODE=release
- Recovery 中间件会 recover 任何 panic。如果有 panic 的话,会写入 500 响应码
- 如果不想使用上面两个默认的中间件,可以使用 gin.New() 新建一个没有任何默认中间件的路由
- gin 中间件中使用 goroutine
- 当在中间件或 handler 中启动新的 goroutine 时,不能使用原始的上下文(c *gin.Context)
- 必须使用其只读副本(c.Copy())