内容目录
责任链模式
是一种行为设计模式,它使得多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。
先看一个示例:
这里使用 Gin 框架实现一个简单的日志记录的中间件
package main
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
)
// 日志记录中间件
func LoggerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 记录请求开始时间
startTime := time.Now()
// 处理请求
c.Next()
// 计算请求处理时间
duration := time.Since(startTime)
// 获取请求的状态码
status := c.Writer.Status()
// 获取请求的客户端IP
clientIP := c.ClientIP()
// 获取请求的方法
method := c.Request.Method
// 获取请求的路径
path := c.Request.URL.Path
// 打印日志
fmt.Printf("[%s] %s %s %s %d %s\n",
startTime.Format("2006-01-02 15:04:05"),
clientIP,
method,
path,
status,
duration,
)
}
}
func main() {
// 创建 Gin 引擎实例
r := gin.Default()
// 注册全局中间件
r.Use(LoggerMiddleware())
// 定义一个简单的 GET 路由
r.GET("/hello", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello, World!",
})
})
// 启动 HTTP 服务
r.Run(":8080")
}
实现原理
在 Gin 中,中间件是一个接受 *gin.Context 作为参数的函数,定义如下:
type HandlerFunc func(*Context)
Gin 使用一个处理函数链(HandlersChain)来存储所有的处理函数,包括中间件和实际的路由处理函数。在 Context 结构体中,定义了一个 handlers 字段用于存储这些处理函数链:
type Context struct {
// 其他字段省略
handlers HandlersChain
index int8
// 其他字段省略
}
其中,HandlersChain 是一个 HandlerFunc 的切片:
type HandlersChain []HandlerFunc
当一个请求到来时,Gin 会按照注册的顺序依次执行处理函数链中的函数。在每个处理函数中,可以调用 c.Next() 方法来执行下一个处理函数:
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
在上述代码中,c.index 表示当前执行到处理函数链中的位置。调用 c.Next() 会递增 c.index,并执行下一个处理函数。这种机制实现了责任链模式,使得请求可以在多个处理函数之间传递,每个处理函数都有机会对请求进行处理或修改。
在 Gin 中,中间件机制正是责任链模式的体现。每个中间件函数都有机会对请求进行处理,如果需要,还可以通过调用 c.Next() 将请求传递给下一个中间件。这种设计使得我们可以灵活地添加、删除或修改中间件,而无需更改核心的请求处理逻辑,可以方便地在请求处理的各个阶段插入自定义的处理逻辑,如日志记录、身份验证、错误处理等。