这里或许是互联网从业者的最后一片净土,随客社区期待您的加入!
您需要 登录 才可以下载或查看,没有账号?立即注册
×
本帖最后由 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)
- }
复制代码优点: 数据在请求生命周期中“透明传递” 避免全局变量 避免在函数层层传递参数
五、最佳实践- func Chain(f http.Handler, middlewares ...func(http.Handler) http.Handler) http.Handler {
- for i := len(middlewares) - 1; i >= 0; i-- {
- f = middlewares[i](f)
- }
- return f
- }
复制代码 使用:
- mux.Handle("/", Chain(http.HandlerFunc(helloHandler),
- loggingMiddleware,
- authMiddleware,
- injectMiddleware,
- ))
复制代码 4. 框架选择
如果项目复杂,可以考虑使用 Gin、Echo 等框架,它们对中间件有更好的封装。
六、总结通过中间件机制,我们可以构建出一个既模块化又高扩展性的 Web 服务。 |