You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Go的net/http包中实现多个中间件与路由处理器的链式调用?

如何在Go的net/http包中实现多个中间件与路由处理器的链式调用?

嘿,看你已经在基于Go的net/http包搭建API,还用上了高阶函数来写路由处理器和中间件,路子完全对!我来帮你把中间件链式调用的部分补全,让你能轻松把多个中间件和路由处理器串起来用~

首先先把你已经写好的代码整理下,方便后续扩展:

import (
    "fmt"
    "net/http"
)

// 你的基础路由处理器,用高阶函数封装
func handleRoute() http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        fmt.Println("Calling route handler")
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("Hello from route handler!"))
    }
}

// 中间件的类型定义,这个你已经写对了
type Middleware func(http.HandlerFunc) http.HandlerFunc

// fooMiddleware 你已经实现了,这里保留
func fooMiddleware() Middleware {
    return func(next http.HandlerFunc) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            fmt.Println("Calling foo middleware before next handler")
            next.ServeHTTP(w, r)
            fmt.Println("Calling foo middleware after next handler")
        }
    }
}

// 补全你提到的barMiddleware,结构和foo类似,可自定义逻辑
func barMiddleware() Middleware {
    return func(next http.HandlerFunc) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            fmt.Println("Calling bar middleware before next handler")
            next.ServeHTTP(w, r)
            fmt.Println("Calling bar middleware after next handler")
        }
    }
}

接下来关键的一步:我们需要一个中间件链式组合函数,把多个中间件和最终的路由处理器“套”在一起。这个函数的作用是从最内层的路由处理器开始,一层一层往外包裹中间件,这样请求进来的时候会按顺序经过各个中间件的前置逻辑,到达处理器后,再按相反顺序经过中间件的后置逻辑,也就是经典的“洋葱模型”。

写一个简单的chain函数就行:

// chain 把多个中间件和最终的处理器组合成一个链式调用的HandlerFunc
func chain(handler http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc {
    // 从最内层的处理器开始,倒序用中间件包裹,保证请求进入时的顺序正确
    for i := len(middlewares) - 1; i >= 0; i-- {
        handler = middlewares[i](handler)
    }
    return handler
}

现在你就可以轻松把多个中间件和路由处理器串起来用了!比如注册路由的时候,直接用这个chain函数组合:

func main() {
    // 组合中间件和路由处理器:请求会先经过foo的前置逻辑,再到bar的前置,最后到路由处理器
    // 处理完后,按bar后置、foo后置的顺序返回
    chainedHandler := chain(handleRoute(), fooMiddleware(), barMiddleware())

    // 注册路由
    http.Handle("/test", chainedHandler)

    // 启动服务
    fmt.Println("Server starting on :8080...")
    http.ListenAndServe(":8080", nil)
}

如果你想封装成createRoute函数来简化注册流程,也很简单:

// createRoute 封装路由注册逻辑,直接传入路径、处理器和中间件,一键完成注册
func createRoute(path string, handler http.HandlerFunc, middlewares ...Middleware) {
    http.Handle(path, chain(handler, middlewares...))
}

// 用createRoute的方式注册路由
func main() {
    createRoute("/test", handleRoute(), fooMiddleware(), barMiddleware())

    fmt.Println("Server starting on :8080...")
    http.ListenAndServe(":8080", nil)
}

现在启动服务,访问http://localhost:8080/test,控制台会输出:

Calling foo middleware before next handler
Calling bar middleware before next handler
Calling route handler
Calling bar middleware after next handler
Calling foo middleware after next handler

完全符合我们预期的执行顺序。

最后提两个小细节:

  • 中间件的顺序很重要:如果你想让barMiddleware先执行前置逻辑,只需要在chain的参数里把它放在前面就行,比如chain(handleRoute(), barMiddleware(), fooMiddleware())。
  • 给中间件传参数也很方便:比如你需要给fooMiddleware传入配置,直接修改高阶函数就行,比如func fooMiddleware(apiKey string) Middleware { ... },调用的时候用fooMiddleware("my-secret-key"),完全不影响链式组合。

备注:内容来源于stack exchange,提问作者baitendbidz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 11:23:05