中间件概念 #

一、中间件概述 #

中间件是处理HTTP请求的拦截器,可以在请求到达处理函数前后执行特定逻辑。

1.1 中间件作用 #

text
┌─────────────────────────────────────────────────────────┐
│                    中间件作用                            │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  请求前处理                                              │
│  ├── 日志记录                                           │
│  ├── 身份认证                                           │
│  ├── 权限验证                                           │
│  ├── 参数验证                                           │
│  └── 限流控制                                           │
│                                                         │
│  请求后处理                                              │
│  ├── 响应修改                                           │
│  ├── 错误处理                                           │
│  ├── 性能统计                                           │
│  └── 响应压缩                                           │
│                                                         │
└─────────────────────────────────────────────────────────┘

1.2 中间件执行流程 #

text
请求 → 中间件1 → 中间件2 → 中间件3 → 处理函数 → 中间件3 → 中间件2 → 中间件1 → 响应
         │          │          │          │          │          │          │
         ▼          ▼          ▼          ▼          ▼          ▼          ▼
       前置处理   前置处理   前置处理   业务逻辑   后置处理   后置处理   后置处理

二、中间件类型 #

2.1 中间件函数签名 #

go
type MiddlewareFunc func(HandlerFunc) HandlerFunc

type HandlerFunc func(Context) error

2.2 中间件级别 #

级别 说明 注册方式
全局中间件 应用于所有路由 e.Use()
路由组中间件 应用于分组路由 g.Use()
路由级中间件 应用于单个路由 e.GET(path, handler, middleware)

2.3 全局中间件 #

go
e := echo.New()

e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Use(middleware.CORS())

e.GET("/users", getUsers)
e.GET("/posts", getPosts)

2.4 路由组中间件 #

go
e := echo.New()

api := e.Group("/api")
api.Use(middleware.JWT([]byte("secret")))

api.GET("/users", getUsers)
api.GET("/posts", getPosts)

2.5 路由级中间件 #

go
e := echo.New()

e.GET("/admin", adminHandler, middleware.JWT([]byte("secret")))

三、中间件原理 #

3.1 洋葱模型 #

text
                    ┌─────────────────┐
                    │   Middleware 1  │
                    │  ┌───────────┐  │
                    │  │Middleware2│  │
                    │  │  ┌─────┐  │  │
                    │  │  │ H │  │  │
                    │  │  │ a │  │  │
                    │  │  │ n │  │  │
                    │  │  │ d │  │  │
                    │  │  │ l │  │  │
                    │  │  │ e │  │  │
                    │  │  │ r │  │  │
                    │  │  └─────┘  │  │
                    │  └───────────┘  │
                    └─────────────────┘

请求 ──────────────────────────────────────────────────▶
    
响应 ◀──────────────────────────────────────────────────

3.2 中间件链 #

go
func middleware1(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        fmt.Println("Middleware 1 - Before")
        err := next(c)
        fmt.Println("Middleware 1 - After")
        return err
    }
}

func middleware2(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        fmt.Println("Middleware 2 - Before")
        err := next(c)
        fmt.Println("Middleware 2 - After")
        return err
    }
}

func handler(c echo.Context) error {
    fmt.Println("Handler")
    return c.String(http.StatusOK, "OK")
}

func main() {
    e := echo.New()
    
    e.Use(middleware1)
    e.Use(middleware2)
    
    e.GET("/", handler)
    
    e.Start(":8080")
}

输出:

text
Middleware 1 - Before
Middleware 2 - Before
Handler
Middleware 2 - After
Middleware 1 - After

3.3 中断中间件链 #

go
func authMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        token := c.Request().Header.Get("Authorization")
        
        if token == "" {
            return echo.NewHTTPError(http.StatusUnauthorized, "请登录")
        }
        
        return next(c)
    }
}

四、内置中间件 #

4.1 Logger - 日志记录 #

go
e.Use(middleware.Logger())

自定义配置:

go
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
    Format: "method=${method}, uri=${uri}, status=${status}\n",
}))

4.2 Recover - 错误恢复 #

go
e.Use(middleware.Recover())

自定义配置:

go
e.Use(middleware.RecoverWithConfig(middleware.RecoverConfig{
    DisablePrintStack: false,
    LogLevel:          1,
}))

4.3 CORS - 跨域处理 #

go
e.Use(middleware.CORS())

自定义配置:

go
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
    AllowOrigins: []string{"https://example.com", "https://api.example.com"},
    AllowMethods: []string{echo.GET, echo.POST, echo.PUT, echo.DELETE},
    AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAuthorization},
}))

4.4 Gzip - 响应压缩 #

go
e.Use(middleware.Gzip())

自定义配置:

go
e.Use(middleware.GzipWithConfig(middleware.GzipConfig{
    Level: 5,
}))

4.5 RateLimiter - 请求限流 #

go
e.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(20)))

4.6 BasicAuth - 基础认证 #

go
e.Use(middleware.BasicAuth(func(username, password string, c echo.Context) (bool, error) {
    if username == "admin" && password == "secret" {
        return true, nil
    }
    return false, nil
}))

4.7 JWT - JWT认证 #

go
e.Use(middleware.JWT([]byte("secret")))

4.8 RequestID - 请求ID #

go
e.Use(middleware.RequestID())

4.9 Secure - 安全头 #

go
e.Use(middleware.Secure())

4.10 Static - 静态文件 #

go
e.Use(middleware.Static("static"))

五、中间件配置 #

5.1 Skipper函数 #

跳过特定路由:

go
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
    Skipper: func(c echo.Context) bool {
        return c.Path() == "/health"
    },
}))

5.2 Before函数 #

请求前处理:

go
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
    Before: func(c echo.Context) {
        c.Set("startTime", time.Now())
    },
}))

六、中间件执行顺序 #

6.1 执行顺序规则 #

text
1. 全局中间件按注册顺序执行
2. 分组中间件在全局中间件之后执行
3. 路由中间件在分组中间件之后执行

6.2 示例说明 #

go
e := echo.New()

e.Use(middleware1)  // 第1个执行
e.Use(middleware2)  // 第2个执行

api := e.Group("/api")
api.Use(middleware3)  // 第3个执行

api.GET("/users", handler, middleware4)  // 第4个执行

执行顺序:

text
middleware1 → middleware2 → middleware3 → middleware4 → handler → middleware4 → middleware3 → middleware2 → middleware1

七、中间件数据传递 #

7.1 使用Context存储 #

go
func authMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        user := &User{ID: 1, Name: "张三"}
        c.Set("user", user)
        return next(c)
    }
}

func handler(c echo.Context) error {
    user := c.Get("user").(*User)
    return c.JSON(http.StatusOK, user)
}

7.2 类型安全获取 #

go
func getUser(c echo.Context) *User {
    if user, ok := c.Get("user").(*User); ok {
        return user
    }
    return nil
}

八、中间件最佳实践 #

8.1 中间件顺序建议 #

go
e := echo.New()

e.Use(middleware.RequestID())     // 1. 请求ID
e.Use(middleware.Recover())       // 2. 错误恢复
e.Use(middleware.Logger())        // 3. 日志记录
e.Use(middleware.Gzip())          // 4. 响应压缩
e.Use(middleware.CORS())          // 5. 跨域处理
e.Use(middleware.RateLimiter())   // 6. 限流
e.Use(middleware.JWT())           // 7. 认证

8.2 中间件组织 #

go
func setupMiddleware(e *echo.Echo) {
    e.Use(middleware.RequestID())
    e.Use(middleware.Recover())
    e.Use(middleware.Logger())
    
    if config.EnableGzip {
        e.Use(middleware.Gzip())
    }
    
    if config.EnableCORS {
        e.Use(middleware.CORSWithConfig(config.CORSConfig))
    }
}

8.3 环境区分 #

go
func setupMiddleware(e *echo.Echo, env string) {
    e.Use(middleware.Recover())
    
    if env == "development" {
        e.Use(middleware.Logger())
    }
    
    if env == "production" {
        e.Use(middleware.Gzip())
        e.Use(middleware.Secure())
    }
}

九、完整示例 #

go
package main

import (
    "fmt"
    "net/http"
    "time"
    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
)

func main() {
    e := echo.New()
    
    e.Use(middleware.RequestID())
    e.Use(middleware.Recover())
    e.Use(middleware.Logger())
    e.Use(middleware.Gzip())
    e.Use(middleware.CORS())
    
    e.Use(timingMiddleware)
    
    e.GET("/", home)
    e.GET("/health", health)
    
    api := e.Group("/api")
    api.Use(authMiddleware)
    
    api.GET("/users", getUsers)
    api.GET("/profile", getProfile)
    
    e.Logger.Fatal(e.Start(":8080"))
}

func timingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        start := time.Now()
        
        err := next(c)
        
        duration := time.Since(start)
        c.Set("duration", duration)
        
        fmt.Printf("Request took %v\n", duration)
        
        return err
    }
}

func authMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        token := c.Request().Header.Get("Authorization")
        
        if token == "" {
            return echo.NewHTTPError(http.StatusUnauthorized, "请登录")
        }
        
        c.Set("userID", "123")
        
        return next(c)
    }
}

func home(c echo.Context) error {
    return c.String(http.StatusOK, "Welcome!")
}

func health(c echo.Context) error {
    return c.JSON(http.StatusOK, map[string]string{
        "status": "healthy",
    })
}

func getUsers(c echo.Context) error {
    return c.JSON(http.StatusOK, []map[string]string{
        {"id": "1", "name": "张三"},
        {"id": "2", "name": "李四"},
    })
}

func getProfile(c echo.Context) error {
    userID := c.Get("userID").(string)
    return c.JSON(http.StatusOK, map[string]string{
        "userID": userID,
        "name":   "张三",
    })
}

十、总结 #

中间件概念要点:

概念 说明
中间件函数 func(HandlerFunc) HandlerFunc
全局中间件 e.Use()
分组中间件 g.Use()
路由中间件 e.GET(path, handler, middleware)
洋葱模型 请求进入、响应返回
数据传递 c.Set() / c.Get()

准备好学习内置中间件了吗?让我们进入下一章!

最后更新:2026-03-28