go资深之路笔记(四)中间件(Middleware)设计模式
介绍:
Web 框架(如 Gin, Echo)的中间件功能非常强大,但理解其底层实现原理更为重要。这是一种经典的装饰器模式(Decorator Pattern) 和 责任链模式(Chain of Responsibility Pattern) 的应用。
核心思想:每个中间件是一个函数,它接收一个 http.Handler 并返回一个新的 http.Handler。这个新的 Handler 可以在调用原始 Handler 之前和之后执行自己的逻辑。
作用: 其实就是将多个相同类型的对象用链表串联起来,执行的时候按顺序执行。比如有三个对象,授权,日志,业务。
串联起来后的顺序是 日志,授权,业务;从而实现,先打印,再授权,通过后再执行业务的逻辑。
代码分段解析:
中间件串联:
type Middleware func(http.Handler) http.Handler // 给函数起别名,这个函数封装一个传参的http.Handler并返回// ApplyMiddleware 将一系列中间件应用到最终的根处理器上(责任链的构建)
func ApplyMiddleware(h http.Handler, middlewares ...Middleware) http.Handler {// 从最后一个中间件开始应用(链的尾部)for i := len(middlewares) - 1; i >= 0; i-- {h = middlewares[i](h) // 假设 a,b,c,d Middleware;最终链表是 a->b->c->d->h}return h
}
中间件函数-日志
// 示例中间件 1: 日志记录
func LoggingMiddleware(next http.Handler) http.Handler {// http.HandlerFunc将 func(ResponseWriter, *Request) 转化成 http.Handlerreturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now()// 在调用下一个处理器之前fmt.Printf("Started %s %s\n", r.Method, r.URL.Path)// 包装 ResponseWriter 以捕获状态码 (可选进阶技巧)lw := &loggingResponseWriter{ResponseWriter: w}next.ServeHTTP(lw, r) // 调用链中的下一个处理器// 在下一个处理器完成之后latency := time.Since(start)fmt.Printf("Completed %s %s with status %d in %v\n",r.Method, r.URL.Path, lw.statusCode, latency) // 打印状态码和其他路由信息})
}// 封装 http.ResponseWriter(主要是多了一个字段 statusCode )
type loggingResponseWriter struct {http.ResponseWriterstatusCode int
}func (lw *loggingResponseWriter) WriteHeader(code int) {lw.statusCode = codelw.ResponseWriter.WriteHeader(code)
}
中间件函数-认证
func AuthMiddleware(next http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {token := r.Header.Get("Authorization")if token != "secret-token" { // token对不上,直接返回http.Error(w, "Unauthorized", http.StatusUnauthorized)return // 中断中间件链,不再向下传递}// 认证通过,继续执行下一个中间件或最终 handlernext.ServeHTTP(w, r)})
}
最终的业务 Handler
func helloHandler(w http.ResponseWriter, r *http.Request) {w.Write([]byte("Hello, World!"))
}
代码示例
// Middleware 类型是一个函数,它接受一个 Handler 并返回一个装饰后的 Handler
type Middleware func(http.Handler) http.Handler// ApplyMiddleware 将一系列中间件应用到最终的根处理器上(责任链的构建)
func ApplyMiddleware(h http.Handler, middlewares ...Middleware) http.Handler {// 从最后一个中间件开始应用(链的尾部)for i := len(middlewares) - 1; i >= 0; i-- {h = middlewares[i](h)}return h
}// 示例中间件 1: 日志记录
func LoggingMiddleware(next http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {start := time.Now()// 在调用下一个处理器之前fmt.Printf("Started %s %s\n", r.Method, r.URL.Path)// 包装 ResponseWriter 以捕获状态码 (可选进阶技巧)lw := &loggingResponseWriter{ResponseWriter: w}next.ServeHTTP(lw, r) // 调用链中的下一个处理器// 在下一个处理器完成之后latency := time.Since(start)fmt.Printf("Completed %s %s with status %d in %v\n",r.Method, r.URL.Path, lw.statusCode, latency)})
}type loggingResponseWriter struct {http.ResponseWriterstatusCode int
}func (lw *loggingResponseWriter) WriteHeader(code int) {lw.statusCode = codelw.ResponseWriter.WriteHeader(code)
}// 示例中间件 2: 认证
func AuthMiddleware(next http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {token := r.Header.Get("Authorization")if token != "secret-token" {http.Error(w, "Unauthorized", http.StatusUnauthorized)return // 中断中间件链,不再向下传递}// 认证通过,继续执行下一个中间件或最终 handlernext.ServeHTTP(w, r)})
}// 最终的业务 Handler
func helloHandler(w http.ResponseWriter, r *http.Request) {w.Write([]byte("Hello, World!"))
}func TestMiddle() {myHandler := http.HandlerFunc(helloHandler)// 按顺序应用中间件:先日志,后认证handlerChain := ApplyMiddleware(myHandler, LoggingMiddleware, AuthMiddleware)http.Handle("/aaa", handlerChain)http.ListenAndServe(":8080", nil)
}
执行程序后
打开网页:
127.0.0.1:8080/aaa/
会输出:
Hello, World!
同时终端打印:
Started GET /aaa
Completed GET /aaa with status 0 in 421.933µs