Gin Handler 与 http.Handler 互转/混用

结论先行

方向一:gin → net/httpgin.Engine 本身实现了 http.Handler,直接 http.ListenAndServe(addr, engine)srv.Handler = engine,还能和标准库 mux 混用。方向二:http.Handler → gin。用 gin.WrapH(h) 包装任意 http.Handler、gin.WrapF(f) 包装 http.HandlerFunc,转成 gin.HandlerFunc 塞进中间件或路由。反向把单个 gin.HandlerFunc 塞进外部 http.Handler 不常见,通常整引擎当 Handler 用即可。

对照表

转换手段
http.Handler → gin.HandlerFuncgin.WrapH(h)
http.HandlerFunc → gin.HandlerFuncgin.WrapF(f)
gin.Engine → http.Handler不需要转,Engine 已实现 ServeHTTP
与 ServeMux 混用mux.Handle("/", engine),其余路径留给 mux

代码示例

// 1) 标准库 handler/handlerFunc 收编进 gin
func oldHealth(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    _, _ = w.Write([]byte("ok"))
}

func main() {
    r := gin.New()
    r.Use(gin.WrapH(someHTTPMiddleware()))     // 整个 http.Handler 当中间件
    r.GET("/healthz", gin.WrapF(oldHealth))    // http.HandlerFunc 直接挂路由
    // 业务路由...

    // 2) 标准库 mux 与 gin 混用(Go 1.22+ 的 ServeMux 也适用)
    mux := http.NewServeMux()
    mux.HandleFunc("/metrics", metricsHandler) // 系统类接口走标准库
    mux.Handle("/", r)                          // 其余全部交给 gin
    _ = http.ListenAndServe(":8080", mux)
}

注意点

  • WrapH/WrapF 包进来的 handler 在 gin 的链内执行:Recovery、日志中间件对它们同样生效(panic 会被外层 Recovery 捕获)。
  • 把 engine 挂到 mux 子路径(如 mux.Handle("/api/", engine))时,gin 收到的 URL.Path 仍带 /api 前缀,需要 gin 侧路由也带该前缀,否则全 404——通常建议 gin 占根路径。
  • 不要手工拼一个 *gin.Context 塞给外部调用:它的生命周期、池化、Keys 都绑定 gin 的请求流程(见第 12 章)。

常见追问 / 记忆点

  • 追问:为什么 gin handler 不能直接当 http.Handler 用?答:签名不同——gin.HandlerFunc 是 func(*gin.Context),http.Handler 是 ServeHTTP(http.ResponseWriter, *http.Request),需要适配层(WrapH/WrapF 就是干这个的)。
  • 追问:迁移老项目到 Gin 怎么渐进?答:新路由用 gin,旧 http.Handler 用 WrapH 挂进来;或反过来 mux 占根、gin 占子前缀(注意前缀问题)。
  • 记忆点:engine 本身就是 http.Handler;收编标准库中间件用 WrapH/WrapF;混用时盯紧 URL 前缀。
笔记加载中…