当前位置: 首页 > news >正文

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

http://www.dtcms.com/a/390569.html

相关文章:

  • MySQL分库分表迁移:ETL平台如何实现数据合并与聚合
  • [极客大挑战 2019]BabySQL
  • SQL-索引使用
  • 数据库和数据仓库有什么区别
  • SpringBoot2.7X整合Swagger、Redission3.X的bug
  • uniapp安卓原生插件实现开启ble Server[外围模式]
  • React 18.2中使用React Router 6.4
  • 人员在岗监测技术研究:基于计算机视觉的智能监管方案
  • 实测AI Ping,一个大模型服务选型的实用工具——行业实践与深度优化策略
  • 通过QuickAPI优化金融系统API:安全快捷的数据共享最佳实践
  • 第4节 添加视频字幕到剪映(Coze扣子空间剪映小助手零基础教程)
  • 算法 --- BFS 解决 FloodFill 算法
  • telnet 一个 ip+端口却无法退出 着急
  • UVa1602/LA3224 Lattice Animals
  • Docker BuildKit 实现 Golang 编译加速
  • [x-cmd] 在 Android 的 Termux 和 iOS 的 iSH 中安装 X-CMD
  • CTFSHOW 中期测评(一)web486 - web501
  • android-USB-STM32
  • 云原生周刊:MetalBear 融资、Chaos Mesh 漏洞、Dapr 1.16 与 AI 平台新趋势
  • Android音频学习(十九)——音频HAL层简介
  • Android之音乐列表播放管理类,控制音乐播放、暂停、播放模式的切换等
  • Docker Compose从入门到实战:配置与命令全指南
  • 10.1 输入子系统模型
  • Unity手游输入笔记
  • SpringCloud-注册中心Nacos[笔记3]
  • 关于MySQL与Python后端命令交互备份
  • 大模型上下文工程实践- 上下文管理策略
  • 资产测绘工具-Nmap
  • 智能体环境配置测试
  • 如何将非结构化文档智能解析高质量数据,并按照阅读顺序还原版面?