Golang中间件的原理与实现
一. 什么是 Middleware?
中间件(Middleware) 是一种 高阶函数,它接受一个函数作为输入,并返回一个经过增强的函数。它的核心思想是通过函数的递归嵌套,动态地为函数添加功能。在 Golang 中,中间件的常见应用场景包括日志记录、权限验证、异常处理、格式化输入输出等。在 python 中叫装饰器
// Middleware 是一个高阶函数,接收一个处理函数 Handler,返回一个经过加工的处理函数 Handler
type Middleware func(Handler) Handler
二. 为什么使用 Middleware ?
在开发中,你可能遇到这样的需求:
-
日志记录:在函数执行前后记录日志,包括输入参数、执行时间等
-
权限验证:根据用户权限决定是否允许执行请求
-
错误处理:捕获函数执行过程中的错误并进行集中处理
假设没有中间件,这段代码可能会变得非常繁琐:
func (s *SomeService) ExampleHandler(ctx context.Context, data string) {
Log(ctx) // 日志操作
if err := Auth(ctx); err != nil { // 检查权限
return err
}
return s.ProcessBusiness(ctx, data) // 核心业务逻辑
}
这种写法使得业务逻辑与额外的附加操作交织在一起,不易维护
通过中间件,我们能够将这些逻辑解耦,使得代码既简洁又清晰:
func (s *SomeService) ExampleHandler(ctx context.Context, data string) {
return s.ProcessBusiness(ctx, data) // 专注于业务处理
}
通过中中间件件的功能插入,业务代码的关注点单一,增强了代码的可读性和可维护性。
三. 中间件调用过程原理
我们大致分为 3 步来讲解其过程
-
实现 Middleware
-
实现 业务处理函数 Handler
-
嵌套 Middlreware , 并包裹 Handler
3.1 实现 Middleware
根据第一章中间件的抽象定义,实现 2 个特化的 中间件
// 抽象中间件
type Middleware func(Handler) Handler
// 日志中间件 的实现
func LogMiddleware(next Handler) Handler {
return func(data string) string {
fmt.Println("--- LogMiddleware: Log Before: ", time.Now())
result := next(data) // 调用下一个处理函数(被捕获的 next)
fmt.Println("--- LogMiddleware: Log After: ", time.Now())
return result
}
}
// 鉴权中间件 的实现
func AuthMiddleware(next Handler) Handler {
return func(data string) string {
fmt.Println(" --- AuthMiddleware: Authing ---")
if data == "坏人" {
return "Access Denied"
}
result := next(data)
fmt.Println(" --- AuthMiddleware: Authing ---")
return result
}
}
3.2 实现 Handler
// 业务处理函数
func BusinessHandler(data string) string {
fmt.Println(" --- 业务 <<" + data + ">> 处理成功")
return data
}
3.3 嵌套 Middlreware , 包裹 Handler
在实际开发中,我们往往需要多个中间件共同作用,比如既要记录日志又要验证权限。这种情况下,需要将多个中间件有序地组合起来。
组合的方式是通过 链式调用(Chain),如下代码展示了如何实现一个 Chain
函数:
func Chain(middlewares ...Middleware) Middleware {
return func(handler Handler) Handler {
// 从后向前, 挨个嵌套中间件
for i := len(middlewares) - 1; i >= 0; i-- {
handler = middlewares[i](handler)
}
return handler
}
}
// 1. 嵌套 Middleware
combinedMiddleware := Chain(LogMiddleware, AuthMiddleware)
// 2. 包裹 Handler
finalHandler := combinedMiddleware(BusinessHandler)
Middleware 的运行过程可以比喻为 “流水线加工”:原始数据经过中间件逐层处理,最终返回加工完成的结果
--- LogMiddleware: Log Before: 2025-03-28 18:21:04.315295 +0800 CST m=+0.000067959
--- AuthMiddleware: Authing ---
--- 业务 <<创建文件>> 处理成功
--- AuthMiddleware: Authing ---
--- LogMiddleware: Log After: 2025-03-28 18:21:04.315417 +0800 CST m=+0.000190084
完整代码
要完整展示前述代码的调用流程,可以参考如下完整例子:
package main
import (
"fmt"
"time"
)
// Middleware: 是一个高阶函数, 接收一个处理函数,输出一个处理后的处理函数
type Middleware func(Handler) Handler
// 日志中间件 的实现
func LogMiddleware(next Handler) Handler {
return func(data string) string {
fmt.Println("--- LogMiddleware: Log Before: ", time.Now())
result := next(data) // 调用下一个处理函数(被捕获的 next)
fmt.Println("--- LogMiddleware: Log After: ", time.Now())
return result
}
}
// 鉴权中间件 的实现
func AuthMiddleware(next Handler) Handler {
return func(data string) string {
fmt.Println(" --- AuthMiddleware: Authing ---")
if data == "坏人" {
return "Access Denied"
}
result := next(data)
fmt.Println(" --- AuthMiddleware: Authing ---")
return result
}
}
// 输入:n个中间件(高阶函数)
// 输出:1个中间件(高阶函数)
// 函数:将n个中间件层层嵌套,1个高阶函数包一个高阶函数
// 意义:这意味着,你传入一个Handler函数,其将会经历Middleware函数的层层处理
func Chain(middlewares ...Middleware) Middleware {
return func(handler Handler) Handler {
// 从后向前, 挨个嵌套中间件
for i := len(middlewares) - 1; i >= 0; i-- {
handler = middlewares[i](handler)
}
return handler
}
}
// ---------------------- Handler --------------------------
// 请求最终的处理函数(在网络中对应的是 http 请求的业务逻辑处理)
type Handler func(data string) string
// 业务处理函数
func BusinessHandler(data string) string {
fmt.Println(" --- 业务 <<" + data + ">> 处理成功")
return data
}
// 使用示例
func main() {
combinedMiddleware := Chain(LogMiddleware, AuthMiddleware)
finalHandler := combinedMiddleware(BusinessHandler)
finalHandler("创建文件")
}