当前位置: 首页 > news >正文

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 步来讲解其过程

  1. 实现 Middleware

  2. 实现 业务处理函数 Handler

  3. 嵌套 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("创建文件")
}

相关文章:

  • Linux 配置NFS服务器
  • Edge浏览器快速开启IE模式
  • MySQL 锁机制全面解析:乐观锁与悲观锁实现及深度剖析
  • ubuntu 2204键盘按键映射修改
  • DataGear 5.3.0 制作支持导出表格数据的数据可视化看板
  • OceanBase的闪回查询功能实践
  • IP数据报报文格式
  • 英伟达「虚拟轨道+AI调度」专利:开启自动驾驶3.0时代的隐形革命
  • 离散的数据及参数适合用什么算法做模型
  • vscode_拼写关闭
  • 从 WPF 到 MAUI:跨平台 UI 开发的进化之路
  • C++使用do {} while(false)的好处
  • 机器学习模型类型
  • OpenCV 图形API(2)为什么需要图形API?
  • 关于《数据资源建设费用测算标准》(工作组讨论稿)意见征集的通知
  • 白盒测试用例的设计
  • 【学Rust写CAD】16 0、1、-1代数单位元(algebraic_units.rs)
  • Redis:概念与常用命令
  • mysql-分区和性能
  • Java项目生成接口文档的方案
  • 甘怀真:天下是神域,不是全世界
  • 加拿大总理访美与特朗普“礼貌交火”
  • 媒体:西安62岁男子当街殴打妻子,警方称打人者已被行拘
  • 印度扩大对巴措施:封锁巴基斯坦名人账号、热门影像平台社媒
  • 澎湃读报丨央媒头版五四青年节集中刊文:以青春之我,赴时代之约
  • 五一假期旅游大市党政领导靠前调度,重视解决游客反映的问题