自定义中间件 #

一、中间件基础结构 #

1.1 基本结构 #

go
func MyMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        return next(c)
    }
}

1.2 使用中间件 #

go
e := echo.New()
e.Use(MyMiddleware)

二、简单中间件示例 #

2.1 日志中间件 #

go
func loggingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        start := time.Now()
        
        err := next(c)
        
        duration := time.Since(start)
        method := c.Request().Method
        path := c.Request().URL.Path
        status := c.Response().Status
        
        log.Printf("[%s] %s %d %v", method, path, status, duration)
        
        return err
    }
}

2.2 计时中间件 #

go
func timingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        start := time.Now()
        
        err := next(c)
        
        duration := time.Since(start)
        c.Response().Header().Set("X-Response-Time", duration.String())
        
        return err
    }
}

2.3 请求ID中间件 #

go
func requestIDMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        requestID := c.Request().Header.Get("X-Request-ID")
        if requestID == "" {
            requestID = uuid.New().String()
        }
        
        c.Response().Header().Set("X-Request-ID", requestID)
        c.Set("requestID", requestID)
        
        return next(c)
    }
}

三、认证中间件 #

3.1 API Key认证 #

go
func apiKeyMiddleware(apiKey string) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            key := c.Request().Header.Get("X-API-Key")
            
            if key == "" {
                return echo.NewHTTPError(http.StatusUnauthorized, "缺少API Key")
            }
            
            if key != apiKey {
                return echo.NewHTTPError(http.StatusUnauthorized, "API Key无效")
            }
            
            return next(c)
        }
    }
}

e.Use(apiKeyMiddleware("your-secret-key"))

3.2 Token认证 #

go
func tokenMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        auth := c.Request().Header.Get("Authorization")
        
        if auth == "" {
            return echo.NewHTTPError(http.StatusUnauthorized, "请登录")
        }
        
        parts := strings.Split(auth, " ")
        if len(parts) != 2 || parts[0] != "Bearer" {
            return echo.NewHTTPError(http.StatusUnauthorized, "Token格式错误")
        }
        
        token := parts[1]
        user, err := validateToken(token)
        if err != nil {
            return echo.NewHTTPError(http.StatusUnauthorized, "Token无效")
        }
        
        c.Set("user", user)
        
        return next(c)
    }
}

3.3 权限验证中间件 #

go
func roleMiddleware(roles ...string) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            user := c.Get("user").(*User)
            
            for _, role := range roles {
                if user.Role == role {
                    return next(c)
                }
            }
            
            return echo.NewHTTPError(http.StatusForbidden, "权限不足")
        }
    }
}

admin := e.Group("/admin")
admin.Use(roleMiddleware("admin"))

四、请求处理中间件 #

4.1 请求体大小限制 #

go
func bodyLimitMiddleware(limit int64) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            contentLength := c.Request().ContentLength
            if contentLength > limit {
                return echo.NewHTTPError(http.StatusRequestEntityTooLarge, "请求体过大")
            }
            
            return next(c)
        }
    }
}

4.2 请求体读取缓存 #

go
func bodyDumpMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        body, err := io.ReadAll(c.Request().Body)
        if err != nil {
            return err
        }
        
        c.Request().Body = io.NopCloser(bytes.NewBuffer(body))
        c.Set("requestBody", string(body))
        
        return next(c)
    }
}

4.3 请求签名验证 #

go
func signatureMiddleware(secret string) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            signature := c.Request().Header.Get("X-Signature")
            timestamp := c.Request().Header.Get("X-Timestamp")
            
            if signature == "" || timestamp == "" {
                return echo.NewHTTPError(http.StatusUnauthorized, "缺少签名")
            }
            
            body, _ := io.ReadAll(c.Request().Body)
            c.Request().Body = io.NopCloser(bytes.NewBuffer(body))
            
            expected := hmacSHA256(secret, timestamp+string(body))
            
            if signature != expected {
                return echo.NewHTTPError(http.StatusUnauthorized, "签名验证失败")
            }
            
            return next(c)
        }
    }
}

五、响应处理中间件 #

5.1 响应包装 #

go
type Response struct {
    Code    int         `json:"code"`
    Message string      `json:"message"`
    Data    interface{} `json:"data,omitempty"`
}

func responseWrapperMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        err := next(c)
        
        if err != nil {
            return err
        }
        
        return nil
    }
}

5.2 响应头添加 #

go
func headersMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        c.Response().Header().Set("X-Server", "Echo")
        c.Response().Header().Set("X-Version", "1.0.0")
        
        return next(c)
    }
}

5.3 响应日志 #

go
func responseLogMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        rw := &responseWriter{ResponseWriter: c.Response().Writer}
        c.Response().Writer = rw
        
        err := next(c)
        
        log.Printf("Response: %s", rw.body)
        
        return err
    }
}

type responseWriter struct {
    http.ResponseWriter
    body []byte
}

func (w *responseWriter) Write(b []byte) (int, error) {
    w.body = append(w.body, b...)
    return w.ResponseWriter.Write(b)
}

六、可配置中间件 #

6.1 配置结构 #

go
type AuthConfig struct {
    Skipper    middleware.Skipper
    TokenLookup string
    AuthScheme  string
    Key         string
}

func AuthWithConfig(config AuthConfig) echo.MiddlewareFunc {
    if config.Skipper == nil {
        config.Skipper = middleware.DefaultSkipper
    }
    
    if config.TokenLookup == "" {
        config.TokenLookup = "header:Authorization:Bearer "
    }
    
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            if config.Skipper(c) {
                return next(c)
            }
            
            token := extractToken(c, config.TokenLookup)
            
            if token == "" {
                return echo.NewHTTPError(http.StatusUnauthorized, "请登录")
            }
            
            user, err := validateToken(token, config.Key)
            if err != nil {
                return echo.NewHTTPError(http.StatusUnauthorized, "Token无效")
            }
            
            c.Set("user", user)
            
            return next(c)
        }
    }
}

6.2 使用配置 #

go
e.Use(AuthWithConfig(AuthConfig{
    Skipper: func(c echo.Context) bool {
        return c.Path() == "/login" || c.Path() == "/register"
    },
    TokenLookup: "header:Authorization:Bearer ",
    Key:         "secret-key",
}))

七、中间件组合 #

7.1 链式组合 #

go
func chain(middlewares ...echo.MiddlewareFunc) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        for i := len(middlewares) - 1; i >= 0; i-- {
            next = middlewares[i](next)
        }
        return next
    }
}

e.Use(chain(
    loggingMiddleware,
    timingMiddleware,
    authMiddleware,
))

7.2 条件组合 #

go
func conditionalMiddleware(condition func(c echo.Context) bool, middleware echo.MiddlewareFunc) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        wrapped := middleware(next)
        return func(c echo.Context) error {
            if condition(c) {
                return wrapped(c)
            }
            return next(c)
        }
    }
}

e.Use(conditionalMiddleware(
    func(c echo.Context) bool {
        return strings.HasPrefix(c.Path(), "/api")
    },
    authMiddleware,
))

八、中间件最佳实践 #

8.1 错误处理 #

go
func errorHandlingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        err := next(c)
        
        if err != nil {
            if he, ok := err.(*echo.HTTPError); ok {
                return c.JSON(he.Code, map[string]interface{}{
                    "code":    he.Code,
                    "message": he.Message,
                })
            }
            
            return c.JSON(http.StatusInternalServerError, map[string]interface{}{
                "code":    500,
                "message": "服务器内部错误",
            })
        }
        
        return nil
    }
}

8.2 Panic恢复 #

go
func panicRecoverMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) (err error) {
        defer func() {
            if r := recover(); r != nil {
                err = fmt.Errorf("panic: %v", r)
                log.Printf("Panic recovered: %v\n%s", r, debug.Stack())
            }
        }()
        return next(c)
    }
}

8.3 上下文传递 #

go
func contextMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        ctx := context.WithValue(c.Request().Context(), "requestID", c.Get("requestID"))
        c.SetRequest(c.Request().WithContext(ctx))
        return next(c)
    }
}

九、完整示例 #

go
package main

import (
    "context"
    "log"
    "net/http"
    "time"
    "github.com/google/uuid"
    "github.com/labstack/echo/v4"
)

type User struct {
    ID   string `json:"id"`
    Name string `json:"name"`
    Role string `json:"role"`
}

func main() {
    e := echo.New()
    
    e.Use(requestIDMiddleware)
    e.Use(loggingMiddleware)
    e.Use(timingMiddleware)
    e.Use(errorHandlingMiddleware)
    
    e.GET("/health", health)
    
    api := e.Group("/api")
    api.Use(authMiddleware)
    
    api.GET("/users", getUsers)
    api.GET("/profile", getProfile)
    
    admin := api.Group("/admin")
    admin.Use(roleMiddleware("admin"))
    admin.GET("/stats", getStats)
    
    e.Logger.Fatal(e.Start(":8080"))
}

func requestIDMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        requestID := uuid.New().String()
        c.Response().Header().Set("X-Request-ID", requestID)
        c.Set("requestID", requestID)
        return next(c)
    }
}

func loggingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        start := time.Now()
        
        err := next(c)
        
        log.Printf("[%s] %s %s %d %v",
            c.Get("requestID"),
            c.Request().Method,
            c.Request().URL.Path,
            c.Response().Status,
            time.Since(start),
        )
        
        return err
    }
}

func timingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        start := time.Now()
        
        err := next(c)
        
        c.Response().Header().Set("X-Response-Time", time.Since(start).String())
        
        return err
    }
}

func errorHandlingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        err := next(c)
        
        if err != nil {
            if he, ok := err.(*echo.HTTPError); ok {
                return c.JSON(he.Code, map[string]interface{}{
                    "code":    he.Code,
                    "message": he.Message,
                })
            }
            
            return c.JSON(http.StatusInternalServerError, map[string]interface{}{
                "code":    500,
                "message": "服务器内部错误",
            })
        }
        
        return nil
    }
}

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, "请登录")
        }
        
        user := &User{
            ID:   "1",
            Name: "张三",
            Role: "admin",
        }
        
        c.Set("user", user)
        
        return next(c)
    }
}

func roleMiddleware(role string) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            user := c.Get("user").(*User)
            
            if user.Role != role {
                return echo.NewHTTPError(http.StatusForbidden, "权限不足")
            }
            
            return next(c)
        }
    }
}

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 {
    user := c.Get("user").(*User)
    return c.JSON(http.StatusOK, user)
}

func getStats(c echo.Context) error {
    return c.JSON(http.StatusOK, map[string]int{
        "users": 100,
    })
}

十、总结 #

自定义中间件要点:

要点 说明
函数签名 func(HandlerFunc) HandlerFunc
前置处理 next(c) 之前执行
后置处理 next(c) 之后执行
数据传递 c.Set() / c.Get()
错误处理 返回 echo.HTTPError
可配置 使用配置结构体

准备好学习中间件执行顺序了吗?让我们进入下一章!

最后更新:2026-03-28