gin中间件学习笔记

发布于:2025-03-23 ⋅ 阅读:(22) ⋅ 点赞:(0)

Gin 框架的中间件

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

一、中间件的核心机制

洋葱模型(Onion Model)

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

请求 → 中间件1 → 中间件2 → 业务逻辑 → 中间件2 → 中间件1 → 响应

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

上下文(gin.Context)

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

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

二、中间件的分类与使用

1. 全局中间件

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

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

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

2. 路由组中间件

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

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

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

3. 局部中间件

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

r.GET("/profile", AuthMiddleware, ProfileHandler)

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

三、中间件的使用示例

单独注册中间件
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 方法,这些方法其实都可以叫中间件

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(): 调用该函数会立即终止当前中间件函数的执行,并且不会再调用后续的中间件函数或路由处理函数

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(): 调用该函数会将控制权交给下一个中间件函数,如果没有下一个中间件函数,则将控制权交给处理请求的路由处理函数

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

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 的前缀

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()
}

路由分组注册中间件

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()
}

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