mrkong 发表于 2025-9-16 13:49:28

轻松掌握GoWeb中间件

本帖最后由 mrkong 于 2025-9-16 13:52 编辑

在 Web 开发中,中间件 (Middleware) 是一个非常重要的概念。它能在 HTTP 请求到达最终处理程序 (Handler) 之前或之后执行一些通用逻辑,比如 日志记录、认证鉴权、跨域处理、请求上下文传递 等。在 Go 语言中,标准库 net/http 提供了最基础的 HTTP 服务器实现。中间件的思想并没有直接体现在 net/http 中,但我们可以很容易地通过函数组合来实现。
一、什么是中间件?中间件本质上就是一个函数:
[*]接收一个 http.Handler
[*]返回一个新的 http.Handler
[*]在调用下一个处理器之前/之后加入自己的逻辑
这样就能在不改变业务代码的情况下,给整个请求链路增加“横切功能”。
二、简单的中间件示例
下面写一个最基础的 日志中间件,用于打印请求的进入和完成:package main

import (
      "log"
      "net/http"
)

// 日志中间件
func loggingMiddleware(next http.Handler) http.Handler {
      return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                // 请求进入时的日志
                log.Printf("➡️ Received request: %s %s", r.Method, r.URL.Path)

                // 执行下一个处理器
                next.ServeHTTP(w, r)

                // 请求完成时的日志
                log.Printf("✅ Request completed: %s %s", r.Method, r.URL.Path)
      })
}

// 最终处理器
func helloHandler(w http.ResponseWriter, r *http.Request) {
      w.Write([]byte("Hello, World!"))
}

func main() {
      mux := http.NewServeMux()
      mux.Handle("/", loggingMiddleware(http.HandlerFunc(helloHandler)))

      log.Println("Server started on :8080")
      http.ListenAndServe(":8080", mux)
}
三、中间件链式调用
中间件最大的优势就是 链式组合。例如在日志中间件的基础上,再加一个认证中间件:// 认证中间件
func authMiddleware(next http.Handler) http.Handler {
      return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                authHeader := r.Header.Get("Authorization")
                if authHeader != "Bearer secret_token" {
                        http.Error(w, "Unauthorized", http.StatusUnauthorized)
                        return
                }
                next.ServeHTTP(w, r)
      })
}

func main() {
      mux := http.NewServeMux()

      // 多个中间件组合:先认证,再日志
      finalHandler := loggingMiddleware(authMiddleware(http.HandlerFunc(helloHandler)))
      mux.Handle("/", finalHandler)

      log.Println("Server started on :8080")
      http.ListenAndServe(":8080", mux)
}
请求执行顺序:
[*]loggingMiddleware 进入日志
[*]authMiddleware 检查认证
[*]helloHandler 处理业务
[*]authMiddleware 返回
[*]loggingMiddleware 记录完成日志

四、中间件间共享数据 —— 使用 Context有时需要在多个中间件和最终处理器之间共享数据(如请求 ID、用户信息)。Go 推荐通过 context.Context 来传递。package main

import (
      "context"
      "fmt"
      "net/http"
)

// 数据注入中间件
func injectMiddleware(next http.Handler) http.Handler {
      return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                // 设置上下文值
                ctx := context.WithValue(r.Context(), "user_id", 123)
                ctx = context.WithValue(ctx, "trace_id", "abc-xyz-001")

                // 将新的 context 注入 request
                r = r.WithContext(ctx)

                // 调用下一个处理器
                next.ServeHTTP(w, r)
      })
}

// 使用数据的处理器
func handler(w http.ResponseWriter, r *http.Request) {
      userID := r.Context().Value("user_id").(int)
      traceID := r.Context().Value("trace_id").(string)

      fmt.Fprintf(w, "Received request, user_id=%d trace_id=%s", userID, traceID)
}

func main() {
      mux := http.NewServeMux()
      mux.Handle("/", injectMiddleware(http.HandlerFunc(handler)))

      fmt.Println("Server started on :8080")
      http.ListenAndServe(":8080", mux)
}
优点:
[*]数据在请求生命周期中“透明传递”
[*]避免全局变量
[*]避免在函数层层传递参数

五、最佳实践
[*]中间件职责单一
每个中间件只处理一个功能,比如日志、鉴权、限流、恢复(panic recover)等。
[*]注意中间件顺序

[*]认证要早(拦截非法请求)
[*]日志通常最外层(能捕获所有请求和结果)
[*]恢复中间件(recover)建议放在最外层,保证 panic 不会崩溃服务。
[*]链式封装工具函数
为了简化中间件组合,可以写一个工具函数:
func Chain(f http.Handler, middlewares ...func(http.Handler) http.Handler) http.Handler {
    for i := len(middlewares) - 1; i >= 0; i-- {
      f = middlewares(f)
    }
    return f
}
使用:
mux.Handle("/", Chain(http.HandlerFunc(helloHandler),
    loggingMiddleware,
    authMiddleware,
    injectMiddleware,
))
    4. 框架选择
      如果项目复杂,可以考虑使用 Gin、Echo 等框架,它们对中间件有更好的封装。
六、总结
[*]中间件是 Go Web 开发中的“插拔式逻辑”。
[*]通过函数组合实现链式调用,能让业务代码更简洁。
[*]使用 context.Context 可以在请求链中传递数据。
[*]合理安排中间件顺序,能提高安全性和可维护性。
通过中间件机制,我们可以构建出一个既模块化又高扩展性的 Web 服务。
页: [1]
查看完整版本: 轻松掌握GoWeb中间件