马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
中心件详解
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() 方法注册,作用于全部路由。
- 调用 Use() 方法,将中心件追加到 Engine 的默认 handlers 中。
- 在每次哀求处置惩罚时,默认中心件会起首实验。
- func (group *RouterGroup) Use(middleware ...HandlerFunc) *RouterGroup {
- group.Handlers = append(group.Handlers, middleware...) // 注册中间件
- return group
- }
- r := gin.Default() // gin.Default() 内置了 Logger 和 Recovery 全局中间件
复制代码 路由级中心件
路由级中心件通过在特定路由上链式调用 Use() 方法注册。
- 路由级中心件注册时,生存在当前路由的 HandlersChain 中。
- 在匹配到具体路由时,这些中心件会被参加到 Context 的 handlers 中,并按序次实验。
- 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)
复制代码 实验序次如下:
- 假如调用 c.Next(),继续实验下一个中心件。
- 假如调用 c.Next(),实验 finalHandler。
克制流程
某个中心件不调用 c.Next():
- func middleware1(c *gin.Context) {
- c.JSON(401, gin.H{"error": "unauthorized"})
- c.Abort() // 停止后续中间件执行
- }
复制代码
- 调用 c.Abort() 会克制后续中心件或处置惩罚函数的实验。
- 标志位 c.index 被设置为最大值。
代码示例
- 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
复制代码- 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
复制代码- 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 提高性能
- }
复制代码
- RouterGroup:
- methodTrees:
- 生存路由信息的焦点,methodTrees 是一组 Radix 树,差别 HTTP 方法(如 GET、POST)对应一棵独立的路由树。
- 每个树节点存储路由路径的部分信息,并链接随处置惩罚函数。
- handlers:
- 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() 调用下一个中心件。
总的流程:
- 根据 HTTP 方法,从 methodTrees 中选择对应的 Radix 树。
- 利用路径递归查找匹配的节点。
- 匹配乐成后,归并全局中心件、分组中心件和路由级中心件,形成完备的 HandlersChain。
- 按链式序次调用中心件。
- 通过 Context 的方法天生 HTTP 相应。
|