返回列表 发布新帖
查看: 6|回复: 0

轻松掌握GoWeb中间件

发表于 昨天 13:49 | 查看全部 |阅读模式

这里或许是互联网从业者的最后一片净土,随客社区期待您的加入!

您需要 登录 才可以下载或查看,没有账号?立即注册

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

在 Web 开发中,中间件 (Middleware) 是一个非常重要的概念。它能在 HTTP 请求到达最终处理程序 (Handler) 之前或之后执行一些通用逻辑,比如 日志记录、认证鉴权、跨域处理、请求上下文传递 等。
在 Go 语言中,标准库 net/http 提供了最基础的 HTTP 服务器实现。中间件的思想并没有直接体现在 net/http 中,但我们可以很容易地通过函数组合来实现。

一、什么是中间件?
中间件本质上就是一个函数:
  • 接收一个 http.Handler
  • 返回一个新的 http.Handler
  • 在调用下一个处理器之前/之后加入自己的逻辑

这样就能在不改变业务代码的情况下,给整个请求链路增加“横切功能”。

二、简单的中间件示例
下面写一个最基础的 日志中间件,用于打印请求的进入和完成:
  1. package main

  2. import (
  3.         "log"
  4.         "net/http"
  5. )

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

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

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

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

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

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

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

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

  17.         log.Println("Server started on :8080")
  18.         http.ListenAndServe(":8080", mux)
  19. }
复制代码
请求执行顺序:
  • loggingMiddleware 进入日志
  • authMiddleware 检查认证
  • helloHandler 处理业务
  • authMiddleware 返回
  • loggingMiddleware 记录完成日志


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

  2. import (
  3.         "context"
  4.         "fmt"
  5.         "net/http"
  6. )

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

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

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

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

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

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

  28.         fmt.Println("Server started on :8080")
  29.         http.ListenAndServe(":8080", mux)
  30. }
复制代码
优点:
  • 数据在请求生命周期中“透明传递”
  • 避免全局变量
  • 避免在函数层层传递参数


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

    • 认证要早(拦截非法请求)
    • 日志通常最外层(能捕获所有请求和结果)
    • 恢复中间件(recover)建议放在最外层,保证 panic 不会崩溃服务。

  • 链式封装工具函数
    为了简化中间件组合,可以写一个工具函数:

  1. func Chain(f http.Handler, middlewares ...func(http.Handler) http.Handler) http.Handler {
  2.     for i := len(middlewares) - 1; i >= 0; i-- {
  3.         f = middlewares[i](f)
  4.     }
  5.     return f
  6. }
复制代码
使用:
  1. mux.Handle("/", Chain(http.HandlerFunc(helloHandler),
  2.     loggingMiddleware,
  3.     authMiddleware,
  4.     injectMiddleware,
  5. ))
复制代码
    4. 框架选择
        如果项目复杂,可以考虑使用 Gin、Echo 等框架,它们对中间件有更好的封装。
六、总结
  • 中间件是 Go Web 开发中的“插拔式逻辑”。
  • 通过函数组合实现链式调用,能让业务代码更简洁。
  • 使用 context.Context 可以在请求链中传递数据。
  • 合理安排中间件顺序,能提高安全性和可维护性。

通过中间件机制,我们可以构建出一个既模块化又高扩展性的 Web 服务。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Copyright © 2001-2025 Suike Tech All Rights Reserved. 随客交流社区 (备案号:津ICP备19010126号) |Processed in 0.125873 second(s), 8 queries , Gzip On, MemCached On.
关灯 在本版发帖返回顶部
快速回复 返回顶部 返回列表