6. gin 中间件和路由
把请求的处理函数叫做中间件,且形参必须是*gin.Context 一个请求可以有多个中间件
通过Use()函数来调用中间件
6.1 单个路由和中间件
-
Next() 会将当前中间件前后分离,前面的为请求中间件,后面的为响应中间件
执行完前面遇到Next()就执行下一个中间件,把响应中间件压入栈中(大概效果是这样)。 -
Abort() 会拦截执行后面的中间件,但当前中间件后面的代码仍会执行完
func m1(c *gin.Context) {
fmt.Println("m1 in")
c.Next()
c.Abort() // 此时Abort在Next后面,m1的剩余部分是最后一个被执行的,之后没有其他中间件
fmt.Println("m1 out")
}
func m2(c *gin.Context) {
fmt.Println("m2 in")
c.Next()
fmt.Println("m2 out")
}
func m3(c *gin.Context) {
fmt.Println("m3 in")
c.Next()
fmt.Println("m3 out")
}
func m4(c *gin.Context) {
fmt.Println("m4 in")
c.Next()
fmt.Println("m4 out")
}
func m5(c *gin.Context) {
fmt.Println("m5 in")
c.Next()
fmt.Println("m5 out")
}
func main() {
router := gin.Default()
// 单个中间件
router.GET("/single", m1, m2, m3)
/*
结果:
m1 in
m2 in
m3 in
m3 out
m2 out
m1 out
*/
// 使用全局中间件 不需要匹配路径,直接用
router.Use(m4, m5)
err := router.Run(":80")
if err != nil {
panic(err)
}
}
- 通过中间件传递数据并接收 key-value
type User struct {
Name string
Age int
}
func _set(c *gin.Context) {
// 设置kv
c.Set("note", "这是一条kv设置")
// 设置结构体数据
c.Set("user", User{
Name: "kjasn",
Age: 11,
})
}
func main() {
router := gin.Default()
// 使用全局中间件 不需要匹配路径,直接用
router.Use(_set)
router.GET("/setVal", func(c *gin.Context) {
// 用Get()接收 形参是key 返回any类型的val和一个bool值表示是否存在key
// 接收单独设置的kv
note, ok := c.Get("note")
if ok {
fmt.Println(note)
}
////////////////////////////////////////////////////////////
// 接收结构体 kv
user, ok := c.Get("user")
if ok {
// 直接打印整个user
fmt.Println(user)
c.JSON(http.StatusOK, gin.H{"data": user})
// 单独打印 需要断言为User类型才能访问User类型的属性
fmt.Println("断言")
_user, ok := user.(User) // 断言成功 _user 接收到user的类型,若失败则为空
if ok {
fmt.Println("姓名:", _user.Name, "年龄:", _user.Age)
c.JSON(http.StatusOK, gin.H{"name": _user.Name, "age": _user.Age})
} else {
fmt.Println("断言失败,不是User类型")
}
}
})
err := router.Run(":80")
if err != nil {
panic(err)
}
}
6.2 路由分组
将一系列的路由放到一个组下,统一管理,为分组路由定义全局中间件同单个路由定义全局中间件一样,用Use()函数
- 定义一些需要的结构体
// 封装响应格式
type Response struct {
Code int `json:"code"`
Date any `json:"date"`
Message string `json:"msg"`
}
type UserInfo struct {
Name string `json:"name"`
Age int `json:"age"`
}
type ArticleInfo struct {
Title string `json:"title"`
Content string `json:"content"`
}
- 定义中间件
// 此处直接内定一些简单的数据
func DisplayUserList(c *gin.Context) {
list := []UserInfo{
{"张三", 23},
{"李四", 45},
}
c.JSON(http.StatusOK, Response{Code: 1, Date: list, Message: "请求成功"})
}
func DisplayArticle(c *gin.Context) {
list := []ArticleInfo {
{"c语言入门到入土", "本书是c语言入门教程,面向入土"},
{"数据库从删库到跑路", "本书教你如何从删库到跑路"},
}
c.JSON(http.StatusOK, Response{Code: 1, Date: list, Message: "请求成功"})
}
func showTest(c *gin.Context) {
fmt.Println("showTest in ")
c.JSON(http.StatusOK, gin.H{"msg": "这是一个用来测试的中间件"})
}
- 拆分出来,这样也方便单独成包
func UserRouterInit(api *gin.RouterGroup) {
// 用户管理分组 将userManagement作为api下的一个组 同一个组内的请求一般放在一个大括号里
// 发出请求 http://127.0.0.1/api/user-management/user
userManagement := api.Group("user-management") {
// 以下是userManagement分组下的一些请求
userManagement.GET("/user", DisplayUserList)
// ......
}
}
func ArticleRouterInit(api *gin.RouterGroup) {
// 文章管理分组 将article-management作为api下的一个组 与userManagement并列
// 发出请求 http://127.0.0.1/api/article-management/article
article := api.Group("article-management") {
// 以下是article-management分组下的一些请求,就像之前写的一样 此处简写
article.GET("/article", DisplayArticle)
// ..........
}
}
func Test(api *gin.RouterGroup) {
// 为test组注册showTest全局中间件,该组下的子组都会用到这个中间件
test := api.Group("test").Use(showTest) {
test.GET("show1", func(c *gin.Context) {
fmt.Println("in show1")
})
test.GET("show2", func(c *gin.Context) {
fmt.Println("in show2")
})
}
}
- 在main()中分组然后直接调用拆分出来的包即可
分组之后,请求当前组下的其他分组需要加上父组的路径作为前缀
// 分组
api := router.Group("api") // 返回一个组
UserRouterInit(api)
ArticleRouterInit(api)
Test(api)
6.3 讲讲gin.Default()和gin.New()
-
gin.New() 是创建一个新的空白引擎,没有添加任何中间件。而**gin.Default()中调用了gin.New()并添加两个中间件 gin.Logger(), gin.Recovery(),这是二者的主要区别。 **
-
gin.Logger() : 记录请求日志:在每次请求到达服务器时,记录请求的信息,包括请求的方法(GET、POST 等)、请求的路径、请求的 IP 地址等。
-
gin.Recovery() : 恢复从错误中恢复:在处理请求时,捕获潜在的异常,比如代码出现了意外的错误或崩溃。它会确保应用不会因为异常而完全崩溃,而是尽量将异常信息记录下来,并返回一个友好的错误响应给客户端 。
// 以下就等同于用gin.Default()
router := gin.New()
router.Use(gin.Logger(), gin.Recovery())