touka/docs/middleware.md
wjqserver 58fd877ae2 docs: 修复审查意见,统一术语并补充注册顺序说明
- 补充中间件注册顺序说明(必须在路由定义之前)
- 统一术语:'组中间件' → '路由组中间件'
- 统一流程图术语
2026-04-21 18:32:10 +08:00

4.4 KiB
Raw Permalink Blame History

中间件 (Middleware)

中间件是运行在 HTTP 请求处理链中的函数。它们可以拦截请求、修改数据、控制流向(通过 c.Next()c.Abort()),并执行通用的前置/后置逻辑。

如何使用中间件

全局中间件

全局中间件应用于所有注册的路由。

r := touka.New()
r.Use(touka.Recovery()) // 崩溃恢复
r.Use(MyCustomLogger()) // 自定义日志

路由组中间件

仅应用于特定组下的路由。

api := r.Group("/api")
api.Use(AuthMiddleware())
{
    api.GET("/user", handleUser)
}

也可以在创建组时直接传入中间件:

api := r.Group("/api", AuthMiddleware(), RateLimitMiddleware())
{
    api.GET("/user", handleUser)
    api.POST("/data", handleData)
}

路由级中间件

为单个路由注册中间件,仅对该路由生效。

// 单个路由中间件
r.GET("/protected", AuthMiddleware(), func(c *touka.Context) {
    c.String(http.StatusOK, "Protected content")
})

// 多个路由中间件(按顺序执行)
r.POST("/upload",
    RateLimitMiddleware(),
    AuthMiddleware(),
    PermissionCheckMiddleware(),
    func(c *touka.Context) {
        // 处理上传
    },
)

// 路由组中的单个路由也可以使用路由级中间件
api := r.Group("/api")
api.GET("/admin", AdminAuthMiddleware(), adminHandler)

编写自定义中间件

中间件的函数签名是 touka.HandlerFunc

示例:请求计时器

func TimerMiddleware() touka.HandlerFunc {
    return func(c *touka.Context) {
        // --- 前置逻辑 ---
        start := time.Now()

        // 执行处理链中的下一个函数
        c.Next()

        // --- 后置逻辑 ---
        duration := time.Since(start)
        log.Printf("Request %s %s took %v", c.Request.Method, c.Request.URL.Path, duration)
    }
}

示例:简单的 API 密钥验证

func APIKeyAuth() touka.HandlerFunc {
    return func(c *touka.Context) {
        apiKey := c.GetReqHeader("X-API-KEY")
        if apiKey != "secret-token" {
            // 验证失败,返回错误并中止后续逻辑
            c.JSON(http.StatusUnauthorized, touka.H{"error": "Invalid API Key"})
            c.Abort()
            return
        }

        // 验证通过,继续执行
        c.Next()
    }
}

中间件执行顺序

理解中间件的执行顺序对于构建正确的处理流程至关重要。注意:注册顺序决定了执行逻辑,中间件必须在注册路由之前调用(全局中间件应在创建组或定义路由前注册)。中间件按照以下顺序执行:

// 全局中间件
r.Use(GlobalMiddleware1())
r.Use(GlobalMiddleware2())

// 组中间件
api := r.Group("/api", GroupMiddleware1())
api.Use(GroupMiddleware2())

// 路由级中间件
api.GET("/users", RouteMiddleware1(), RouteMiddleware2(), userHandler)

对于 /api/users 请求,执行顺序为:

  1. GlobalMiddleware1() - 全局中间件
  2. GlobalMiddleware2() - 全局中间件
  3. GroupMiddleware1() - 路由组中间件
  4. GroupMiddleware2() - 路由组中间件
  5. RouteMiddleware1() - 路由级中间件
  6. RouteMiddleware2() - 路由级中间件
  7. userHandler - 最终处理函数
请求进入 → 全局中间件 → 路由组中间件 → 路由级中间件 → 最终处理函数 → 路由级中间件后置逻辑 → 路由组中间件后置逻辑 → 全局中间件后置逻辑 → 响应

内置中间件

  • Recovery: 捕获任何发生的 panic恢复运行并返回 500 错误。它还负责调用全局错误处理器。

Touka 的设计非常精简,许多扩展功能(如 Gzip, JWT, Sessions由外部或第三方库提供您可以轻松通过 r.Use() 集成它们。

条件中间件 (Conditional Middleware)

Touka 支持根据布尔条件动态启用或禁用中间件。这在根据环境配置启用插件时非常有用。

UseIf

// 仅在 Debug 模式下启用日志
r.Use(r.UseIf(config.IsDebug, MyDebugLogger))

UseChainIf (条件中间件链)

如果您有一组相关的中间件需要根据同一条件启用,可以使用 UseChainIf

r.Use(r.UseChainIf(config.EnableMetrics,
    MetricsMiddleware,
    PrometheusMiddleware,
    MonitoringMiddleware,
))

这些方法利用了 MiddlewareXFunc(即返回 HandlerFunc 的工厂函数),确保中间件实例按需创建或高效复用。