作为装饰器的中间件
本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 97 课,共 107 课。
中间件是 Go 中装饰器模式最实用的应用之一。在 Web 开发中,中间件函数会包装 HTTP 处理程序,以添加日志记录、身份验证或计时等横切关注点,而无需修改原始处理程序。
关键在于,中间件接收一个处理器,并返回一个添加了新行为的处理器。这正是装饰器模式:输入和输出的接口相同,但功能得到增强:
type Handler func(request string) string
func LoggingMiddleware(next Handler) Handler {
return func(request string) string {
result := next(request)
return "[LOG] " + result
}
}
func AuthMiddleware(next Handler) Handler {
return func(request string) string {
return "[AUTH] " + next(request)
}
}每个中间件都会包装链中的下一个处理器,在调用被包装的处理器之前或之后添加自己的行为。你可以通过组合多个中间件来将它们叠加起来:
func MainHandler(request string) string {
return "Response to: " + request
}
handler := LoggingMiddleware(AuthMiddleware(MainHandler))
fmt.Println(handler("GET /users"))
// [LOG] [AUTH] 对: GET /users的响应包装的顺序决定了执行顺序:最外层的中间件会先运行。这种模式广泛用于 Go Web 框架的请求处理管道中,因此你可以轻松添加或移除功能,而无需修改核心处理程序逻辑。
挑战
简单让我们使用“中间件装饰器”模式来构建请求处理流水线!你将创建一个系统,让中间件函数包装处理程序,从而添加计时、验证和格式化等功能,同时不修改核心处理程序的逻辑。
你会将代码分成 3 个文件:
handler.go:定义处理程序类型,以及用于处理请求的基础处理程序。创建一个
Handler类型,它是一个接收请求字符串并返回响应字符串的函数:type Handler func(request string) string实现一个
BaseHandler函数,让它对接收到的任何请求都返回Processed: [request]。middleware.go:创建用于包装处理程序并添加额外行为的中间件函数。编写 3 个中间件函数,每个函数接收一个
Handler,并返回一个新的Handler:TimingMiddleware:为响应添加计时信息,返回[TIMING] [response]ValidationMiddleware:为响应添加验证前缀,返回[VALID] [response]UppercaseMiddleware:将整个响应转换为大写
每个中间件都应调用被包装的处理程序,并增强它的结果。
main.go:通过组合中间件来构建处理流水线。读取要应用的中间件层数。然后,对每一层读取中间件类型(
timing、validation或uppercase),并用该中间件包装当前处理程序。按读取顺序应用中间件:第一个读到的中间件会成为最外层包装器。构建流水线后,读取请求字符串,通过组合后的处理程序处理请求,并打印结果。
输入如下:
- 第 1 行:中间件层数
- 后续各行:每行一个中间件类型(
timing、validation或uppercase) - 最后一行:要处理的请求
例如,给定:
2
timing
validation
GET /users输出应为:
[TIMING] [VALID] Processed: GET /users再例如,给定:
3
uppercase
timing
validation
POST /data输出应为:
[TIMING] [VALID] PROCESSED: POST /DATA再例如,给定:
1
uppercase
hello world输出应为:
PROCESSED: HELLO WORLD再例如,给定:
0
simple request输出应为:
Processed: simple request注意,中间件的包装顺序会影响输出:第一个应用的中间件(最外层)会先运行,并接收到所有内层中间件处理后的最终结果。在第二个示例中,大写中间件最后运行(位于最内层),因此它会先将基础响应转换为大写,然后计时和验证中间件再添加各自的前缀!
亲手试试
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// 读取中间件层数
nLine, _ := reader.ReadString('\n')
n, _ := strconv.Atoi(strings.TrimSpace(nLine))
// 读取中间件类型
middlewareTypes := make([]string, n)
for i := 0; i < n; i++ {
line, _ := reader.ReadString('\n')
middlewareTypes[i] = strings.TrimSpace(line)
}
// 读取请求
request, _ := reader.ReadString('\n')
request = strings.TrimSpace(request)
// TODO:从 BaseHandler 开始
// TODO:用中间件包装处理器,构建处理流程
// 按顺序应用中间件,先读取的中间件成为最外层包装
// 提示:按逆序遍历 middlewareTypes,即可实现这一点
// 最后应用的中间件位于最外层(最先运行)
// TODO:通过组合后的处理器处理请求,并打印结果
fmt.Println("TODO: implement pipeline")
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Go 在线编译器