如何在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
相关产品推荐
相关产品推荐

