做web开发都会用到中间件,大家对中间件有多少了解呢?
近期在使用goland的轻量级框架gin,研究了其中的中间件的源码,受此启发实现一个简单的中间件。
达到的效果
递归调用,使用logger中 ,调用recovery,调用业务函数
逻辑.png
创建一个结构体与中间件的函数类型
// 中间件的函数类型
type HandleFunc func(*Request)
// 执行函数的结构体重点
type Request struct {
index int `desc:"索引控制中间件的"`
middleware []HandleFunc `desc:"存放所有的中间件"`
}
将中间件添加到结构体
// @title 添加中间件
// @description 将需要添加的中间件添加到执行切片中
// @param middlewares ...HandleFunc "符合中间件的类型"
func (r *Request) Register(middlewares ...HandleFunc) {
r.middleware = append(r.middleware, middlewares...)
}
执行函数
// @title 执行中间件
// @description 回掉执行所有的中间件
func (r *Request) Next() {
// TODO 将索引数自增
r.index++
// TODO 检测索引是否超过中间件的数量
if r.index >= len(r.middleware) {
return
}
// TODO 执行下一个中间件
r.middleware[r.index](r)
}
这里着重讲解下request.index字段的用处。
中间件模式是递归的,不是组合方式,这是与插件模式的重要区别。
第一个中间件函数的实现中,需要递归的调用request.Next唤起下一个中间件函数,如此不断的递归向后,直到request.index到达request.middlewares的末尾,则递归终止。
递归的好处就是靠前的中间件是包裹着靠后的中间件的,这种自顶向下的结构可以实现非常灵活的框架设计。
执行我们的中间件
// @title log中间件
// @description 记录执行顺序
// @param r *Request "请求结构体"
func logger(r *Request) {
fmt.Println("我是第一个中间件,开始")
// TODO 执行下一个中间件 执行完毕后返回执行下一个
r.Next()
fmt.Println("执行结束")
}
// @title 恢复中间件
// @description 获取panic防止程序崩溃
// @param r *Request "请求结构体"
func recovery(r *Request) {
defer func() {
if err := recover(); err != nil {
log.Println("出错了")
}
}()
r.Next()
}
func main() {
r := NewRequest()
// TODO 注册函数
r.Register(logger,recovery)
r.Next()
}
总结
上面我们自己手写了一个中间件模式,大体的流程如上,
- 主要是要明白index的作用,他是控制我们中间件的执行顺序
- Gegister函数是用于添加我们的中间件函数,顺序与添加的顺序一致
- Next是出发我们的下一个中间件的函数,同时会把index自增
这块的设计还是很巧妙,可细细品味
网友评论