为你自己学Go
  • README
  • PART01.Web框架概览
    • 1.01 Web框架概览:学习路线
    • 1.02 Web框架概览-Beego框架分析
    • 1.03 Web框架概览-GIN框架分析
    • 1.04 Web框架概览-Iris框架分析
    • 1.05 Web框架概览-Echo框架分析
  • PART02.Server
    • 2.01 Server详解与面试要点
  • PART03.路由树
    • 3.01 路由树-Beego&GIN&Echo实现与设计总结
    • 3.02 路由树-全静态匹配
    • 3.03 路由树-TDD起步
    • 3.04 路由树-静态匹配测试用例
    • 3.05 路由树-静态匹配之路由查找
    • 3.06 路由树-静态匹配之集成Server
    • 3.07 路由树-通配符匹配之路由注册
    • 3.08 路由树-通配符匹配之路由查找与测试
    • 3.09 路由树-参数路径之基本注册和查找
    • 3.10 路由树-参数路径之校验
    • 3.11 路由树-参数路径之参数值
    • 3.12 路由树-总结与面试要点
  • PART04.课后复习
    • 4.01 课后复习-Server
    • 4.02 课后复习-Route
  • PART05.Context
    • 5.01 Context-简介
    • 5.02 Context-Beego Context设计分析
    • 5.03 Context-Gin Context设计分析
    • 5.04 Context-Echo和Iris的Context设计分析
    • 5.05 Context-处理输入输出总结
    • 5.06 Context-处理输入之Body输入
    • 5.07 Context-处理输入之表单输入
    • 5.08 Context-处理输入之查询参数、路径参数和StringValue
    • 5.09 Context-处理输出
    • 5.10 Context-总结与面试要点
  • PART06.AOP
    • 6.01 AOP简介与不同框架设计概览
    • 6.02 AOP设计方案-Middleware
  • PART07.Middleware
    • 7.01 Middleware-AccessLog
    • 7.02 Middleware-Trace简介和OpenTelemetry
    • 7.03 Middleware-OpenTelemetry测试
    • 7.04 Middleware-OpenTelemetry总结
    • 7.05 Prometheus详解
    • 7.06 Middleware-Prometheus
    • 7.07 Middleware-错误页面
    • 7.08 Middleware-从panic中恢复
    • 7.09 Middleware总结和面试
  • PART08.Review
    • 8.01 课后复习-AOP
    • 8.02 课后复习-Context
    • 8.03 课后复习-Middleware-AccessLog
  • PART09.Appendix
    • 附录1.责任链模式
    • 附录2.生成器模式
    • 附录3.函数选项模式
  • xiaochengxu
    • 01.原力去水印
    • 02.KeePass密码管理:安全轻松的管理您的密码
Powered by GitBook
On this page
  • PART1. 非集中式的设计方案--洋葱模式
  • 1.1 定义Middleware
  • 1.2 编排Middleware的顺序
  • PART2. 测试
  1. PART08.Review

8.01 课后复习-AOP

Last updated 9 months ago

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

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结束执行
责任链模式简介