Golang实现一个基础的中间件示例

Go HTTP中间件是接收并返回http.Handler的函数,通过闭包在原handler前后插入逻辑;需调用next.ServeHTTP以确保链式执行,顺序影响执行流程,配置应作为参数传入避免共享,panic恢复中间件须置于最外层。

什么是 Go HTTP 中间件

Go 的中间件本质是函数套函数:接收一个 http.Handler,返回一个新的 http.Handler,在调用原 handler 前后插入逻辑。它不是框架内置概念,而是基于 http.Handler 接口和闭包的自然延伸。

最简中间件写法(日志示例)

核心是实现 func(http.Handler) http.Handler 签名。注意别漏掉 next.ServeHTTP(w, r),否则请求就断了。

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Printf("Started %s %s", r.Method, r.URL.Path)
        next.ServeHTTP(w, r) // 必须调用,否则下游 handler 不会执行
        log.Printf("Completed %s %s", r.Method, r.URL.Path)
    })
}

使用时链式包装:

handler := http.HandlerFunc(yourHandler)
handler = loggingMiddleware(handler)
handler = authMiddleware(handler) // 可叠加多个
http.ListenAndServe(":8080", handler)

带参数的中间件(如允许跨域)

中间件本身可以接受配置参数,返回“中间件工厂函数”。常见错误是把配置写死在闭包外,导致所有实例共享同一份配置。

  • allowedOrigins 应作为参数传入,而非全局变量
  • 每个 CORS() 调用返回独立的中间件实例
  • 注意 w.Header().Set() 必须在 next.ServeHTTP 之前设置响应头,否则可能被覆盖
func CORS(allowedOrigins []string) func(http.Handler) http.Handler {
    return func(next

http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { origin := r.Header.Get("Origin") if len(allowedOrigins) == 0 || contains(allowedOrigins, origin) { w.Header().Set("Access-Control-Allow-Origin", origin) w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type") } if r.Method == "OPTIONS" { w.WriteHeader(http.StatusOK) return } next.ServeHTTP(w, r) }) } } func contains(slice []string, s string) bool { for _, v := range slice { if v == s { return true } } return false }

中间件顺序很重要

中间件从外到内执行:第一个包装的最先运行,最后一个包装的最后运行(但它的前置逻辑在 next.ServeHTTP 前,后置逻辑在之后)。比如:

  • loggingMiddleware(authMiddleware(handler)):先打日志 → 再鉴权 → 再执行 handler → 再打完成日志
  • 如果鉴权失败,next.ServeHTTP 不会被调用,后续中间件和 handler 都不会执行
  • panic 恢复中间件必须放在最外层,才能捕获所有内层 panic

容易忽略的是:中间件内部的 http.Error 或直接写 w.WriteHeader 后再调用 next.ServeHTTP,可能导致重复写 header 报错 http: multiple response.WriteHeader calls