对责任链模式不太了解的读者建议先翻看一下责任链模式简介
PART1. 非集中式的设计方案--洋葱模式
1.1 定义Middleware
middleware.go
:
package web
// Middleware 中间件
type Middleware func(HandleFunc) HandleFunc
这种入参和返回值均为函数类型的设计,是函数式编程.
1.2 编排Middleware的顺序
实际上这一步就是将多个Middleware组建成一条责任链
1.2.1 定义中间件链
httpServer.go
:
package web
import (
"net"
"net/http"
)
// HTTPServer HTTP服务器
type HTTPServer struct {
router // router 路由树
middlewares []Middleware // middlewares Server级别的中间件链 实际上就是责任链 所有的请求都会经过这个链的处理
}
1.2.2 构建中间件链
需要注意的是,构建中间件链的顺序,和中间件的执行顺序是相反的.换言之,也就是说最后一个被执行的中间件是最先被组装到责任链上的
httpServer.go
:
package web
import (
"net"
"net/http"
)
// HTTPServer HTTP服务器
type HTTPServer struct {
router // router 路由树
middlewares []Middleware // middlewares Server级别的中间件链 实际上就是责任链 所有的请求都会经过这个链的处理
}
// ServeHTTP WEB框架入口
func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 构建上下文
ctx := &Context{
Req: r,
Resp: w,
}
// 构建责任链
// step1. 找到请求对应的HandleFunc
root := s.serve
for i := len(s.middlewares) - 1; i >= 0; i-- {
// step2. 从后往前构建责任链
// Tips: 组装的过程是从后向前的 也就是说最后一个被执行的中间件是最先被组装到责任链上的
root = s.middlewares[i](root)
}
// 从责任链的头部开始执行
root(ctx)
}
这里之所以要先找到请求对应的HandleFunc,是因为请求在经过了整条中间件链后,最终还是要去执行这个HandleFunc.
你可能有所疑问的地方在于:如果我找不到请求对应的HandleFunc,那为什么还要走这一整条中间件链?
我个人的理解是:因为这条中间件链是Server级别的.就如同Nginx的access.log,即使访问了一个不存在的url,同样也会在access.log中记录一条404的日志
PART2. 测试
middleware_test.go
:
package web
import (
"fmt"
"net/http"
"testing"
)
// Test_Middleware 测试中间件的工作顺序
func Test_Middleware(t *testing.T) {
s := NewHTTPServer()
s.middlewares = []Middleware{
Middleware1,
Middleware2,
Middleware3,
Middleware4,
}
s.ServeHTTP(nil, &http.Request{})
}
func Middleware1(next HandleFunc) HandleFunc {
return func(ctx *Context) {
fmt.Println("中间件1开始执行")
next(ctx)
fmt.Println("中间件1结束执行")
}
}
func Middleware2(next HandleFunc) HandleFunc {
return func(ctx *Context) {
fmt.Println("中间件2开始执行")
next(ctx)
fmt.Println("中间件2结束执行")
}
}
func Middleware3(next HandleFunc) HandleFunc {
return func(ctx *Context) {
fmt.Println("中间件3中断后续中间件的执行")
}
}
func Middleware4(next HandleFunc) HandleFunc {
return func(ctx *Context) {
fmt.Println("中间件4不会被执行")
}
}
测试结果:
中间件1开始执行
中间件2开始执行
中间件3中断后续中间件的执行
中间件2结束执行
中间件1结束执行
Last updated