当前位置: 首页 > article >正文

【GO基础学习】Gin 框架中间件的详解

文章目录

  • 中间件详解
  • 中间件执行
    • 全局中间件
    • 路由级中间件
    • 运行流程
      • 中间件的链式执行
      • 中断流程
    • 代码示例
  • gin框架总结


中间件详解

Gin 框架中间件是其核心特性之一,主要用于对 HTTP 请求的处理进行前置后置的逻辑插入,例如日志记录、身份认证、错误处理等。

我们在创建默认的gin引擎时:r := gin.Default()

func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery())  // 默认注册的两个中间件
	return engine
}

通过Use()函数注册了Logger中间件和Recovery中间件,Use()函数:

// 也就是说,通过 Use() 连接的中间件将被
// 包含在每个请求的处理程序链中。即使是 404、405、静态文件...
// 例如,这里适合放置日志记录器或错误管理中间件。
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
	engine.RouterGroup.Use(middleware...)
	engine.rebuild404Handlers()
	engine.rebuild405Handlers()
	return engine
}

实际上还是调用的RouterGroup的Use函数:

// 使用将中间件添加到组中。
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
	group.Handlers = append(group.Handlers, middleware...)
	return group.returnObj()
}

注册中间件其实就是将中间件函数追加到group.Handlers中。

type RouterGroup struct {
	Handlers HandlersChain
	basePath string
	engine   *Engine
	root     bool
}

type HandlersChain []HandlerFunc

// HandlerFunc 定义了 gin 中间件作为返回值使用的处理程序。
type HandlerFunc func(*Context)

跟踪这个结构找到HandlersChain ,这是 Gin 中保存中间件和处理函数的核心数据结构,中间件和最终处理函数都以 HandlerFunc 的形式存储在链中。

Context 是 Gin 中非常重要的对象,负责在中间件之间传递数据:

type Context struct {
    Request        *http.Request    // 当前的 HTTP 请求
    Writer         http.ResponseWriter // 响应输出
    handlers       HandlersChain    // 当前请求的中间件链
    index          int8             // 当前执行到的中间件索引
    Keys           map[string]any   // 自定义上下文数据存储
    // 其他字段...
}

核心字段

  • handlers: 保存当前路由的中间件链。
  • index: 标记当前执行到第几个中间件。
  • Keys: 用于存储自定义的上下文数据,在中间件之间共享。

注册路由时,会将对应路由的函数和之前的中间件函数结合到一起:
handlers = group.combineHandlers(handlers) // 将处理请求的函数与中间件函数结合


中间件执行

Gin 中间件分为全局中间件和路由级中间件两种,最终都保存在 HandlersChain 中。

全局中间件

全局中间件通过 Engine.Use() 方法注册,作用于所有路由。

  1. 调用 Use() 方法,将中间件追加到 Engine 的默认 handlers 中。
  2. 在每次请求处理时,默认中间件会首先执行。
func (group *RouterGroup) Use(middleware ...HandlerFunc) *RouterGroup {
    group.Handlers = append(group.Handlers, middleware...) // 注册中间件
    return group
}

r := gin.Default() // gin.Default() 内置了 Logger 和 Recovery 全局中间件


路由级中间件

路由级中间件通过在特定路由上链式调用 Use() 方法注册。

  1. 路由级中间件注册时,保存在当前路由的 HandlersChain 中。
  2. 在匹配到具体路由时,这些中间件会被加入到 Contexthandlers 中,并按顺序执行。
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
    handlers = group.combineHandlers(handlers) // 合并路由级中间件和处理函数
    group.engine.addRoute("GET", path, handlers)
    return group.returnObj()
}

运行流程

中间件的链式执行

Gin 使用 Context 中的 index 字段来控制中间件的链式调用:

  • index 表示当前执行到的中间件索引。
  • 每个中间件需要调用 c.Next() 才能执行下一个中间件。
  • 如果某个中间件不调用 c.Next(),后续的中间件和处理函数将不会执行(终止流程)。
func (c *Context) Next() {
    c.index++
    for c.index < int8(len(c.handlers)) {
        c.handlers[c.index](c) // 执行下一个中间件或处理函数
        c.index++
    }
}

通过索引遍历HandlersChain链条,从而实现依次调用该路由的每一个函数(中间件或处理请求的函数)。

以一个请求路径为例,假设 /hello 路径注册了两个中间件和一个处理函数:

r.GET("/hello", middleware1, middleware2, finalHandler)

执行顺序如下:

  1. 进入 middleware1
  • 如果调用 c.Next(),继续执行下一个中间件。
  1. 进入 middleware2
  • 如果调用 c.Next(),执行 finalHandler
  1. 返回时按链路逆序执行剩余代码。

中断流程

某个中间件不调用 c.Next()

func middleware1(c *gin.Context) {
    c.JSON(401, gin.H{"error": "unauthorized"})
    c.Abort() // 停止后续中间件执行
}

  • 调用 c.Abort() 会终止后续中间件或处理函数的执行。
  • 标志位 c.index 被设置为最大值。

代码示例

  1. 全局中间件
package main

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

func Logger() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 执行前
        fmt.Println("Logger - Before Request")
        c.Next() // 执行下一个中间件
        // 执行后
        fmt.Println("Logger - After Request")
    }
}

func main() {
    r := gin.New()

    // 注册全局中间件
    r.Use(Logger())

    r.GET("/hello", func(c *gin.Context) {
        c.String(200, "Hello World")
    })

    r.Run(":8080")
}

执行流程:

Logger - Before Request
Hello World
Logger - After Request
  1. 路由级中间件
package main

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

func Middleware1() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("Middleware1 - Before")
        c.Next()
        fmt.Println("Middleware1 - After")
    }
}

func Middleware2() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("Middleware2 - Before")
        c.Next()
        fmt.Println("Middleware2 - After")
    }
}

func main() {
    r := gin.New()

    r.GET("/test", Middleware1(), Middleware2(), func(c *gin.Context) {
        fmt.Println("Final Handler")
        c.String(200, "OK")
    })

    r.Run(":8080")
}

执行流程:

Middleware1 - Before
Middleware2 - Before
Final Handler
Middleware2 - After
Middleware1 - After
  1. 流程中断
func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        if token == "" {
            c.JSON(401, gin.H{"error": "unauthorized"})
            c.Abort() // 中断流程
            return
        }
        c.Next()
    }
}

r.GET("/secure", AuthMiddleware(), func(c *gin.Context) {
    c.String(200, "Secure Data")
})


gin框架总结

Engine 是 Gin 框架的核心数据结构,负责路由注册、HTTP 请求处理,以及中间件的全局管理。

type Engine struct {
    RouterGroup            // 继承了 RouterGroup,用于路由分组和中间件管理
    handlers     HandlersChain // 全局中间件链
    methodTrees  methodTrees   // 路由树存储,不同 HTTP 方法对应一棵 Radix 树
    ContextPool  sync.Pool     // 上下文对象池,复用 Context 提高性能
}
  1. RouterGroup:
    • 用于路由分组。
    • 支持分组级别的中间件注册。
  2. methodTrees:
    • 保存路由信息的核心,methodTrees 是一组 Radix 树,不同 HTTP 方法(如 GET、POST)对应一棵独立的路由树。
    • 每个树节点存储路由路径的部分信息,并链接到处理函数。
  3. handlers:
    • 全局中间件链,所有路由共享的中间件。
  4. ContextPool:
    • 通过对象池复用 Context,减少内存分配和垃圾回收的开销。

路由树构建:methodTrees 是路由信息存储的核心结构,不同 HTTP 方法各自维护一棵 Radix 树。

type methodTrees []methodTree

type methodTree struct {
    method string // HTTP 方法,如 GET、POST
    root   *node  // Radix 树的根节点
}
  • Gin 在注册路由时,使用路径分段递归构建 Radix 树。

  • 每个路由的处理函数和中间件保存在树节点的 handlers 字段中。

func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
    // 找到对应的 Radix 树
    root := engine.methodTrees.get(method).root
    root.addRoute(path, handlers)
}

RouterGroup 用于路由的分组和中间件管理,通过分组可以为一组路由统一添加中间件或前缀。

type RouterGroup struct {
    Handlers HandlersChain // 路由分组级中间件
    basePath string        // 分组路径前缀
    engine   *Engine       // 指向顶层的 Engine
}
  • 分组路由:通过 Group() 方法创建子分组,支持嵌套分组。
  • 注册中间件:分组级中间件通过 Use() 方法注册,作用范围为该分组及其子分组。

HandlersChain 是一个中间件链表,保存所有的中间件和处理函数,按顺序执行。

type HandlersChain []HandlerFunc

type HandlerFunc func(*Context)
  • 接收 *Context 参数,在 HTTP 请求的生命周期中共享数据。

  • 可通过调用 c.Next() 继续执行下一个中间件。

Context 是 Gin 中最重要的组件之一,用于在中间件和处理函数之间传递数据。

type Context struct {
    Request        *http.Request    // 当前请求
    Writer         http.ResponseWriter // 当前响应
    handlers       HandlersChain    // 当前中间件链
    index          int8             // 当前中间件执行位置
    Keys           map[string]any   // 用于存储用户自定义数据
    // 其他字段...
}

中间件链控制

  • 调用 c.Next() 执行下一个中间件。
  • 调用 c.Abort() 中断执行链。

响应输出

  • 通过 c.JSON()c.String() 等方法生成 HTTP 响应。

自定义数据存储

  • c.Set()c.Get() 用于在中间件间共享数据。

Gin 的中间件机制非常灵活,支持全局、分组、路由级中间件。

  • 中间件链以 HandlersChain 的形式保存。
  • 链式调用,每个中间件通过 c.Next() 调用下一个中间件。

总的流程:

  1. 路由匹配
  • 根据 HTTP 方法,从 methodTrees 中选择对应的 Radix 树。
  • 使用路径递归查找匹配的节点。
  1. 中间件执行
  • 匹配成功后,合并全局中间件、分组中间件和路由级中间件,形成完整的 HandlersChain
  • 按链式顺序调用中间件。
  1. 处理函数执行
  • 执行完所有中间件后,最终调用路由处理函数。
  1. 返回响应
  • 通过 Context 的方法生成 HTTP 响应。


http://www.kler.cn/a/459177.html

相关文章:

  • JeeSite 快速开发平台:全能企业级快速开发解决方案|GitCode 光引计划征文展示
  • Kafka3.x KRaft 模式 (没有zookeeper) 常用命令
  • 使用 httputils + chronicle-bytes 实现金融级 java rpc
  • 我用AI学Android Jetpack Compose之开篇
  • SweetAlert2 - 漂亮可定制的 JavaScript 弹窗
  • STM32 拓展 RTC(实时时钟)
  • Error: The Calculated NPWS= 84330 != The Read NPWS= 84328
  • 优化租赁小程序提升服务效率与用户体验的策略与实践
  • 代码随想录算法训练营第六天 | 242. 有效的字母异位词、349. 两个数组的交集、202. 快乐数、1. 两数之和
  • 如何利用无线路由器实现水泵房远程监测管理
  • 关于科研工具的思考
  • 企业能源物联网数据采集设备
  • WOFOST作物模型(3):敏感性分析
  • 学习鸿蒙第一天
  • shell指令操作
  • 机器学习之逻辑回归算法、数据标准化处理及数据预测和数据的分类结果报告
  • 【VBA】EXCEL - VBA 创建 Sheet 表的 6 种方法,以及注意事项
  • pycharm如何运行js代码详细图文教程
  • Qt/C++案例 记录创建并加载动态链接库修改exe/dll类型文件的版本信息的示例
  • 云手机:Instagram 矩阵搭建方案
  • MySQL什么情况下会加间隙锁?
  • 使用 AI Cursor 编程实现一个小产品 Chrome 扩展插件 MVP 功能
  • 阿里云 ECS 服务器绑定多个公网IP
  • 程序员转行室内设计师(软装设计流程)
  • 打卡算法题:155. 最小栈 --- 从193ms 到 4 ms的优化
  • linux装git