Golang中间件的原理与实现

一. 什么是 Middleware?

中间件(Middleware) 是一种 高阶函数,它接受一个函数作为输入,并返回一个经过增强的函数。它的核心思想是通过函数的递归嵌套,动态地为函数添加功能。在 Golang 中,中间件的常见应用场景包括日志记录、权限验证、异常处理、格式化输入输出等。在 python 中叫装饰器

Go 复制代码
// Middleware 是一个高阶函数,接收一个处理函数 Handler,返回一个经过加工的处理函数 Handler
type Middleware func(Handler) Handler

二. 为什么使用 Middleware ?

在开发中,你可能遇到这样的需求:

  • 日志记录:在函数执行前后记录日志,包括输入参数、执行时间等

  • 权限验证:根据用户权限决定是否允许执行请求

  • 错误处理:捕获函数执行过程中的错误并进行集中处理

假设没有中间件,这段代码可能会变得非常繁琐:

Go 复制代码
func (s *SomeService) ExampleHandler(ctx context.Context, data string) {
    Log(ctx) // 日志操作
    if err := Auth(ctx); err != nil { // 检查权限
        return err
    }
    return s.ProcessBusiness(ctx, data) // 核心业务逻辑
}

这种写法使得业务逻辑与额外的附加操作交织在一起,不易维护

通过中间件,我们能够将这些逻辑解耦,使得代码既简洁又清晰:

Go 复制代码
func (s *SomeService) ExampleHandler(ctx context.Context, data string) {
    return s.ProcessBusiness(ctx, data) // 专注于业务处理
}

通过中中间件件的功能插入,业务代码的关注点单一,增强了代码的可读性和可维护性。

三. 中间件调用过程原理

我们大致分为 3 步来讲解其过程

  1. 实现 Middleware

  2. 实现 业务处理函数 Handler

  3. 嵌套 Middlreware , 并包裹 Handler

3.1 实现 Middleware

根据第一章中间件的抽象定义,实现 2 个特化的 中间件

Go 复制代码
// 抽象中间件
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

Go 复制代码
// 业务处理函数
func BusinessHandler(data string) string {
	fmt.Println("		--- 业务 <<" + data + ">> 处理成功")
	return data
}

3.3 嵌套 Middlreware , 包裹 Handler

在实际开发中,我们往往需要多个中间件共同作用,比如既要记录日志又要验证权限。这种情况下,需要将多个中间件有序地组合起来。

组合的方式是通过 链式调用 (Chain),如下代码展示了如何实现一个 Chain 函数:

Go 复制代码
func Chain(middlewares ...Middleware) Middleware {
	return func(handler Handler) Handler {
		// 从后向前, 挨个嵌套中间件
		for i := len(middlewares) - 1; i >= 0; i-- {
			handler = middlewares[i](handler)
		}
		return handler
	}
}
Go 复制代码
// 1. 嵌套 Middleware
combinedMiddleware := Chain(LogMiddleware, AuthMiddleware)
// 2. 包裹 Handler
finalHandler := combinedMiddleware(BusinessHandler)

Middleware 的运行过程可以比喻为 "流水线加工":原始数据经过中间件逐层处理,最终返回加工完成的结果

Go 复制代码
--- 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

完整代码

要完整展示前述代码的调用流程,可以参考如下完整例子:

Go 复制代码
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("创建文件")
}
相关推荐
ai大师1 分钟前
给聊天机器人装“短期记忆“:Flask版实现指南
后端·python·gpt·flask·oneapi·中转api·apikey
galileo20162 分钟前
rust服务应用开发框架
后端·rust
xyliiiiiL1 小时前
从责任链模式聊到aware接口
java·开发语言
Elec_z2 小时前
网络深处的守门人
开发语言·网络
闪电麦坤953 小时前
C#:Time.deltaTime
开发语言·c#
Alfadi联盟 萧瑶5 小时前
Python-Django入手
开发语言·python·django
codingandsleeping5 小时前
浏览器的缓存机制
前端·后端
-代号95276 小时前
【JavaScript】十二、定时器
开发语言·javascript·ecmascript
勘察加熊人6 小时前
c++实现录音系统
开发语言·c++
self-discipline6346 小时前
【Java】Java核心知识点与相应面试技巧(七)——类与对象(二)
java·开发语言·面试