gin中间件学习笔记

Gin 框架的中间件

Gin 中间件是一种用于处理 HTTP 请求的钩子函数,可以在请求到达业务逻辑处理函数(如路由处理函数)之前或之后执行特定操作。其核心目标是解耦通用功能(如日志、认证、跨域处理)与业务逻辑,提升代码复用性和可维护性。

一、中间件的核心机制

洋葱模型(Onion Model)

Gin 中间件的执行流程类似 "洋葱" 分层结构:请求按中间件注册顺序进入,执行 c.Next() 后跳转到下一中间件或业务逻辑,返回时再以相反顺序执行剩余代码。例如:

plaintext 复制代码
请求 → 中间件1 → 中间件2 → 业务逻辑 → 中间件2 → 中间件1 → 响应

这种机制允许在请求前后执行逻辑(如统计耗时、记录日志)

上下文(gin.Context)

中间件通过 gin.Context 共享数据和控制流程:

  • 数据传递 :使用 c.Set("key", value) 存储数据,c.Get("key") 获取数据(如传递请求 ID)。
  • 流程控制c.Next() 继续执行后续中间件,c.Abort() 终止请求并直接返回响应(如认证失败时)

二、中间件的分类与使用

1. 全局中间件

作用于所有请求,通过 r.Use() 注册。例如:

go 复制代码
r := gin.Default()
r.Use(gin.Logger())  // 内置日志中间件
r.Use(AuthMiddleware) // 自定义认证中间件

常用于日志记录、全局异常捕获等场景。

2. 路由组中间件

仅作用于特定路由组,通过 group.Use() 注册。例如:

go 复制代码
admin := r.Group("/admin")
admin.Use(AuthMiddleware) // 仅对 /admin 路径生效
admin.GET("/dashboard", DashboardHandler)

适用于权限分层(如后台管理接口需认证)。

3. 局部中间件

仅作用于单个路由,直接在路由定义中指定。例如:

go 复制代码
r.GET("/profile", AuthMiddleware, ProfileHandler)

适用于特定接口需要额外校验的场景(如敏感数据接口)。

三、中间件的使用示例

单独注册中间件
go 复制代码
import (
  "fmt"
  "github.com/gin-gonic/gin"
  "net/http"
)
func indexHandler(c *gin.Context) {
  fmt.Println("index.....")
  c.JSON(http.StatusOK, gin.H{
    "msg": "index",
  })
}

//定义一个中间件
func m1(c *gin.Context) {
  fmt.Println("m1 in.........")
}
func main() {
  r := gin.Default()
  //m1处于indexHandler函数的前面,请求来之后,先走m1,再走index
  r.GET("/index", m1, indexHandler)

  _ = r.Run()
}

浏览器访问localhost:8080/index

控制台输出

m1 in...

index...

多个中间件

router.GET 后面可以跟很多 HandlerFunc 方法,这些方法其实都可以叫中间件

go 复制代码
package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
)

func m1(c *gin.Context) {
  fmt.Println("m1 ...in")
}
func m2(c *gin.Context) {
  fmt.Println("m2 ...in")
}

func main() {
  router := gin.Default()

  router.GET("/", m1, func(c *gin.Context) {
    fmt.Println("index ...")
    c.JSON(200, gin.H{"msg": "响应数据"})
  }, m2)

  router.Run(":8080")
}

// m1 ...in
// index ...
// m2 ...in
中间件拦截响应

c.Abort(): 调用该函数会立即终止当前中间件函数的执行,并且不会再调用后续的中间件函数或路由处理函数

go 复制代码
package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
)

func m1(c *gin.Context) {
  fmt.Println("m1 ...in")
  c.JSON(200, gin.H{"msg": "第一个中间件拦截了"})
  c.Abort()
}
func m2(c *gin.Context) {
  fmt.Println("m2 ...in")
}

func main() {
  router := gin.Default()

  router.GET("/", m1, func(c *gin.Context) {
    fmt.Println("index ...")
    c.JSON(200, gin.H{"msg": "响应数据"})
  }, m2)

  router.Run(":8080")
}

// m1 ...in
中间件放行

c.Next(): 调用该函数会将控制权交给下一个中间件函数,如果没有下一个中间件函数,则将控制权交给处理请求的路由处理函数

go 复制代码
package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
)

func m1(c *gin.Context) {
  fmt.Println("m1 ...in")
  c.Next()
  fmt.Println("m1 ...out")
}
func m2(c *gin.Context) {
  fmt.Println("m2 ...in")
  c.Next()
  fmt.Println("m2 ...out")
}

func main() {
  router := gin.Default()

  router.GET("/", m1, func(c *gin.Context) {
    fmt.Println("index ...in")
    c.JSON(200, gin.H{"msg": "响应数据"})
    c.Next()
    fmt.Println("index ...out")
  }, m2)

  router.Run()
}


// m1 ...in
// index ...in
// m2 ...in
// m2 ...out
// index ...out
// m1 ...out
全局注册中间件

使用 Use 去注册全局中间件,Use 接收的参数也是多个 HandlerFunc

go 复制代码
package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
)

func m10(c *gin.Context) {
  fmt.Println("m1 ...in")
  c.Next()
  fmt.Println("m1 ...out")
}

func main() {
  router := gin.Default()

  router.Use(m10)
  router.GET("/", func(c *gin.Context) {
    fmt.Println("index ...in")
    c.JSON(200, gin.H{"msg": "index"})
    c.Next()
    fmt.Println("index ...out")
  })

  router.Run()

}

// m1 ...in
// index ...in
// index ...out
// m1 ...out

路由

路由分组

将一系列的路由放到一个组下,统一管理。例如,以下的路由前面统一加上 api 的前缀

go 复制代码
package main

import "github.com/gin-gonic/gin"

func main() {
  router := gin.Default()

  r := router.Group("/api")
  r.GET("/index", func(c *gin.Context) {
    c.String(200, "index")
  })
  r.GET("/home", func(c *gin.Context) {
    c.String(200, "home")
  })

  router.Run()
}

路由分组注册中间件

go 复制代码
package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
)

func middle(c *gin.Context) {
  fmt.Println("middle ...in")
}

func main() {
  router := gin.Default()

  r := router.Group("/api").Use(middle)  // 可以链式,也可以直接r.Use(middle)
  r.GET("/index", func(c *gin.Context) {
    c.String(200, "index")
  })
  r.GET("/home", func(c *gin.Context) {
    c.String(200, "home")
  })

  router.Run()
}

这样写我们就可以指定哪一些分组下可以使用中间件了

相关推荐
Chef_Chen3 分钟前
从0开始学习R语言--Day18--分类变量关联性检验
学习
键盘敲没电20 分钟前
【IOS】GCD学习
学习·ios·objective-c·xcode
海的诗篇_1 小时前
前端开发面试题总结-JavaScript篇(一)
开发语言·前端·javascript·学习·面试
AgilityBaby1 小时前
UE5 2D角色PaperZD插件动画状态机学习笔记
笔记·学习·ue5
AgilityBaby1 小时前
UE5 创建2D角色帧动画学习笔记
笔记·学习·ue5
武昌库里写JAVA2 小时前
iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
java·开发语言·spring boot·学习·课程设计
一弓虽3 小时前
git 学习
git·学习
Moonnnn.5 小时前
【单片机期末】串行口循环缓冲区发送
笔记·单片机·嵌入式硬件·学习
viperrrrrrrrrr76 小时前
大数据学习(131)-Hive数据分析函数总结
大数据·hive·学习
fen_fen6 小时前
学习笔记(26):线性代数-张量的降维求和,简单示例
笔记·学习·算法