8.01 课后复习-AOP

对责任链模式不太了解的读者建议先翻看一下责任链模式简介

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