☰
langchain 中间件
2026/10/27 10:24:06 网站建设 项目流程

1. 中间件概述

在create_agent( )的底层运行机制中,有几个重要的组件,分别是:

  • 模型(Model):Agent 的“大脑”,负责理解任务与决策推理
  • 工具(Tools):Agent 的“手脚”,执行模型自己做不到的外部操作
  • 系统提示词(System Prompt):Agent 的 “角色”,告诉模型该怎么想、参考什么上下文
  • 中间件(Middleware):Agent 的 “中枢”,在执行流程的关键节点进行拦截、控制和增强

语法如下:

1.1 什么是中间件?

Middleware(中间件):是 Agent 执行过程中的钩子函数

钩子:框架或系统在某些关键执行点暴露的扩展接口;开发者可以“挂上”自己的逻辑,在那些点上插入、修改或替换行为,而无需改变主流程代码;就像在流水线上某个环节设置了一个“检查点”或 “插入器”

借助中间件,开发者可以高度定制和控制 Agent 运行的每一个环节,是处理 Agent 生命周期的标准方式

没有中间件的 agent 框架:

添加中间件之后的Agent架构:

在 LangChain 的 Agent 执行循环中,比如 “模型调用前”、“模型调用后”、“工具调用前后” 设置一些钩子 (hooks),可以在不改 Agent 主体逻辑的情况下实现策略与治理

1.2 为什么需要中间件?

如果没有中间件,Agent 的执行流程通常比较直接:

用户输入 → 拼接提示词/消息 → 调用模型 → 如有需要调用工具 → 返回结果

这种方式只适用于简单需求,一旦进入真实项目,往往会遇到很多额外需求,这些需求有一个共同特点:它们不是 Agent 的核心业务逻辑,但又会影响 Agent 的执行过程;这些逻辑又不能全部直接写进主流程,会造成主程序混乱,代码难以复用等问题

中间件的价值:在于把这些与业务无关、但与执行过程强相关的横切逻辑,从 Agent 主流程中分离出来。让 Agent 主体代码聚焦业务 ,而借助中间件,实现“拦截流程、修改流程、增强流程”

目前,LangChain 1.x 的中间件能实现如下功能:

  • ✅ 日志与分析 - 追踪行为、调试、性能监控
  • ✅ 转换 - 修改提示词、工具选择、输出格式
  • ✅ 容错 - 重试、降级、早期终止
  • ✅ 安* - 限流、守护规则、PII 检测

1.3 中间件的分类

分为:

  • 自定义中间件:允许开发者自定义,从而实现更加灵活的 Agent 行为管理
  • 内置中间件:LangChain 实现并提供的
    • 模型供应商定制的中间件:依赖于特定模型服务的实现
    • 和模型供应商无关的中间件:LangChain提供的与供应商无关的中间件,如下

链接:Middleware overview - Docs by LangChain

1.4 和模型供应商无关的内置中间件分类

LangChain 提供的和模型供应商无关的内置中间件分为六个类别

类型1:成本与资源控制类

核心目标:控成本、控配额、避免无限调用

解决问题:“ Agent太贵、太能跑、停不下来 ” 的问题

包含:

  • Model call limit:限制模型调用次数,防止一次任务反复请求 LLM,导致费用失控
  • Tool call limit:限制工具调用次数,避免 Agent 无限试错、死循环调工具
  • Summarization:在上下文快满时自动总结历史,减少 token 消耗
  • Context editing:裁剪上下文、清理工具调用痕迹,本质上也是为了节省上下文成本

业务场景理解:

适合生产环境的成本治理、配额治理、长会话优化、SaaS 产品控费

类型2:稳定性与容错保障类

核心目标:保证服务不中断、失败后尽量自动恢复

解决问题:“ 调用失败怎么办、模型挂了怎么办、工具超时怎么办 ”

包含:

  • Model fallback:主模型失败时切换备用模型
  • Model retry:模型调用失败后自动重试
  • Tool retry:工具调用失败后自动重试

业务场景理解: 适合线上生产系统,尤其是多模型、多工具依赖的 Agent

本质上是在做高可用、容灾、鲁棒性建设

类型3:安全与合规风控类

核心目标:让 Agent 可控、可审、合规

包含:

  • Human-in-the-loop(人在环):在关键工具调用前暂停,等人工审批
  • PII detection:检测和处理个人敏感信息

业务场景理解:

适合企业内部系统、客服系统、审批流、数据查询类 Agent。

尤其是涉及:发邮件、调数据库、调财务/人事系统、导出敏感信息、执行外部动作等

类型4:决策增强与智能编排类

核心目标:提升 Agent 的决策质量和任务拆解能力

解决问题:“Agent不够聪明、不会规划、不会筛选工具” 的问题

包含:

  • To-do list:给 Agent 增加任务规划、分步骤执行和状态跟踪能力
  • LLM tool selector:当工具太多时,用子模型筛选最相关的几个工具交给主模型
  • Subagent:允许生成子Agent,把复杂任务拆给不同角色处理

业务场景理解:

适合复杂任务流,比如:研究型 Agent、多步骤分析、报告生成、多角色协作、长链路任务编排等

这类本质上是在增强 Agent的“脑子”与“组织能力”

类型5:执行能力扩展类

核心目标:给 Agent 更多“手脚”

解决问题:“Agent 只能聊天,不能真正操作环境”的问题

包含:

  • Shell tool:给 Agent 持久 shell,会执行命令
  • File search:给 Agent 文件搜索能力,能做 Glob/Grep
  • Filesystem:给 Agent 文件系统读写与长期存储能力

业务场景理解:

适合工程 Agent、代码 Agent、本地自动化 Agent、运维 Agent

本质上是把 Agent 从“纯推理”扩展成“能操作环境的执行体”

类型6:开发调试与测试辅助类

核心目标:方便开发、测试、验证 Agent 行为

这类中间件主要不是直接服务业务,而是服务于研发和调试阶段

包含:

  • LLM tool emulator:用 LLM 模拟工具执行,便于测试(最典型)
  • Summarization:有时也可辅助调试长会话表现
  • Context editing:可用于测试上下文裁剪效果
  • Human-in-the-loop:也常用于调试高风险步骤

业务场景理解:

适合开发阶段快速验证流程、做 mock、减少真实工具依赖

2. 常用内置中间件的使用

2.1 SummarizationMiddleware 中间件

作用:对历史消息列表进行摘要&总结,达到压缩上下文的效果

原理:在达到触发条件时,调用大模型对历史消息进行摘要,将摘要的结果作为HumanMessage, 放到消息列表最开始的位置

参数说明如下:

参数1:model — 用于生成摘要的模型

可以是模型名称也可以是模型对象,如果传递的是模型名称,底层会调用init_chat_model初始化模型

参数2:trigger — 摘要触发条件

是一个列表,每个元素对应一个条件,当任一条件满足时,触发摘要:

  • tokens:token 的数量,历史 token 的累计数量达到该值触发摘要
  • messages:历史消息数量,历史消息条数达到该值触发摘要
  • fraction:上下文长度比例,历史 token 的累计数量达到模型的 max_input_tokens*fraction 触发摘要

注意:如果条件包含 fraction ,要求模型的 profile 包含max_input_tokens

参数3:keep — 摘要时保留的原始消息

支持三种条件,但和 trigger 不同,keep同一时间只接收一种条件

  • tokens:摘要时保留的token数量
  • messages:摘要时保留的历史消息条数
  • fraction:摘要时保留 max_input_tokens*fraction 个 token

参数4:token_counter — 统计token数量的函数

默认使用 LangChain 提供的 count_tokens_approximately ,一般不用更改

参数5:summary_prompt — 摘要时的自定义提示词

该提示词需要包含{messages} 占位符,使得历史消息列表可以被插入;不指定则使用内置提示词

参数6:trim_token_to_summarize — 摘要时历史消息的最大token数

如果历史消息 token 数大于该值,则会被裁剪;默认为" 4000 "

如果 trigger 用 token 作为度量,调大触发阈值时,当前配置项应相应调整,否则会丢失信息

案例1:

输出结果如下:

案例2:测试 summary_prompt 参数

输出结果如下:

2.2 HumanInTheLoopMiddleware 中间件

HumanInTheLoopMiddleware(人在环中间件、人工审核中间件):在工具调用前中断Agent运行,等待用户对工具调用请求决策:

  • approve(同意执行)
  • edit(编辑调用配置后执行)
  • reject(拒绝执行)

参数1:interrupt_on — 工具名和对应的中断策略

策略可以是True、False或InterruptOnConfig对象,精细控制决策选项

1. True 表示需要中段,其所有决策 (approve, edit, reject) 都可以选择

2. False 表示不中断,即无需审批即可执行

3. InterruptOnConfig 是一个TypedDict 的子类,可以用字典直接赋值;支持的Key有:

  • allowed_decisions:精细控制中断后允许的决策
  • description:特定工具的中断描述信息,优先级高于 description_prefix,后者会更改所有工具中断的描述

参数2:description_prefix — 自定义中断描述

默认为 "Tool execution requires approval"

案例:

注意:本例需要从中断的位置让 Agent 继续运行,这就需要用到短期记忆,这里先使用即可

创建 Agent 时:通过checkpointer参数启用短期记忆

调用 Agent 时:通过传递相同的config加载记忆

结果大致如下:

案例2:指明工具调用请求决策

最终大致输出结果如下:

2.3 PIIMiddleware 中间件

PII 中间件用于检测和处理对话中的个人身份信息(Personally Identifiable Information,PII),支持自定义处理策略

参数说明如下:

参数1:pii_type — 检测的 PII 数据类型

可以是内置类型或自定义类型,自定义类型有

  • email:电子邮箱地址
  • credit_card:信用卡号
  • url:网址
  • mac_address:设备MAC地址
  • ip:IP地址

参数2:strategy — 处理PII信息的策略

支持四种选项:

  • redact:将检测到的 PII 信息用字符串 [REDACTED_[PII_TYPE]] 替换,其中的 PII_TYPE是上面提到的具体类型,比如 [REDACTED_EMAIL] 、 [REDACTED_CREDIT_CARD] 这样的标签。完全 “擦除/隐藏” 真实内容;适合日志清洗、合规需求、公开输出时隐藏敏感内容
  • mask:用 *** 将 PII 信息的前面一部分信息遮蔽;比如信用卡号可能变成 ****-****- ****-1234 (只保留最后几位/部分可见),邮箱可能保留域名部分 + 隐藏用户名的一部分等 — 既隐藏大部分敏感信息,又保留了一点“可辨识性”(比如账号后四位、域名等),适合用户服务界面 / 前端显示 / 需要部分可识别但不泄露完整敏感内容的场景
  • hash:用检测到的 PII 信息的哈希值替代原值;比如:<email_hash:a1b2c3d4>,适合 analytics、调试 (debug)、统计分析、匿名追踪等场景
  • block:如果检测到 PII 信息, 直接抛出异常。适合对隐私要求极高、绝不允许泄露任何敏感信息的场景

参数3:detector — 自定义PII检测函数或者正则表达式

如果没有提供则使用内置的检测函数(一般不用手动修改)

LangChain 为每种 PII 信息定制了专门的检测函数,相关源码如下:

参数4:apply_to_input — 是否在调用模型前检测

默认为 True

参数5:apply_to_output — 是否在模型调用后检测

默认为 False

参数6:apply_to_tool_results — 是否在工具调用后检测其输出

默认为False

通常只在模型调用前检测;因为 PII 检测的主要目的是避免将敏感信息发送给模型服务导致信息泄露

案例如下:使用内置检测器

输出内容如下:

案例:自定义检测器 / 检测函数

接着:

输出结果如下:

2.4 TodoListMiddleware中间件

TodoListMiddleware 中间件赋予了 Agent任务规划和追踪进度的能力,可以应对复杂的多步任务

典型应用场景:

  • 任务链路长、步骤多,且有严格的先后依赖关系
  • 需要在前端 UI 界面实时展示 Agent 的 “思考与执行进度”

To-do list 的创建和维护是通过调用 write_todos 工具实现的

参数说明如下:

1. system_prompt — 自定义指导 todo 列表使用的提示词

不提供则使用内置提示词,通常不必提供

2. tool_description — 自定义 write_tools 工具的描述信息

不提供则使用内置描述,通常不必提供

案例:

设计一个较复杂任务:

1. 任务目标

扫描工作目录,测试并修复工作区下的my_add.py文件

2. 工具列表

为了实现上述任务,提供一系列工具:

  • list_files:扫描工作目录,列出其中的所有文件
  • read_file:扫描指定文件,返回文件内容
  • write_file:向指定文件写入内容
  • run_tests:运行测试,底层基于 pytest 实现

3.代码

3.1 环境准备

在项目根目录下创建todo_workspace作为工作空间

在该目录下创建my_add.py,写入以下内容:

在该目录下创建test_my_add.py,写入以下内容:

测试工具是基于 pytest 实现的

pytest 用法如下:

1.在PyCharm中打开终端,依次执行命令:切换到 todo_workspace 的工作目录

2. 然后执行:

3. 输出结果如下:

分析:

  1. pytest 会扫描目录下所有以 test_ 开头或以 _test 结尾的文件,视为测试文件
  2. 然后执行测试文件中所有以 test 开头的函数
  3. 执行出错会打印到控制台,如上所示
  4. 测试函数的逻辑是调用 my_add.py 中的 add 函数,得不到符合预期的结果则抛出异常

3.2 业务代码

# 模型初始化 from langchain.chat_models import init_chat_model from dotenv import load_dotenv import os from rich import print as rprint from langchain_core.messages import SystemMessage, HumanMessage, AIMessage # 1.加载配置信息 load_dotenv(override=True) api_key = os.getenv("BAILIAN_API_KEY") base_url = os.getenv("BAILIAN_API_BASE") # 2.初始化模型 llm = init_chat_model( model_provider="openai", # 模型提供商 model="deepseek-v4.1-flash", # 模型名称 api_key=api_key, # API Key base_url=base_url, # API Base URL ) # 提供工具列表 from langchain.tools import tool from pathlib import Path import subprocess WORKSPACE = Path("../todo_workspace") @tool def list_files(path: str = ".") -> str: """ 列出工作区指定目录下的文件和子目录, path 只能是相对路径 Args: path: 工作区下的相对路径,一定指向目录,默认为., 表示工作区根路径, 不能访问工作区外的目录 """ target = (WORKSPACE / path).resolve() workspace_root = WORKSPACE.resolve() if not str(target).startswith(str(workspace_root)): return "错误:只允许访问工作区内的目录。" if not target.exists(): return f"错误:目录不存在: {path}" if not target.is_dir(): return f"错误:不是目录: {path}" items = sorted(target.iterdir(), key=lambda p: (p.is_file(),p.name.lower())) if not items: return f"目录为空: {path}" lines = [] for item in items: rel = item.relative_to(workspace_root) kind = "[DIR]" if item.is_dir() else "[FILE]" lines.append(f"{kind} {rel.as_posix()}") return "\n".join(lines) @tool def read_file(path: str) -> str: """ 读取工作区中的文本文件内容。path 只能是相对路径。 Args: path: 工作区内的文件名 """ file_path = (WORKSPACE / path).resolve() if not str(file_path).startswith(str(WORKSPACE.resolve())): return "错误:只允许读取工作区内的文件。" if not file_path.exists(): return f"错误:文件不存在: {path}" return file_path.read_text(encoding="utf-8") @tool def write_file(path: str, content: str) -> str: """ 写入工作区中的文本文件。path 只能是相对路径 Args: path: 工作区内的文件名 content: 写入文件的内容 """ file_path = (WORKSPACE / path).resolve() if not str(file_path).startswith(str(WORKSPACE.resolve())): return "错误:只允许写入工作区内的文件。" file_path.write_text(content, encoding="utf-8") return f"已写入文件: {path}" @tool def run_tests() -> str: """ 在工作区运行 pytest -q,并返回输出。不接收任何参数,返回格式为 returncode=0|1 STDOUT: STDERR: """ try: result = subprocess.run( ["pytest", "-q"], cwd=str(WORKSPACE), capture_output=True, text=True, timeout=20, ) return ( f"returncode={result.returncode}\n\n" f"STDOUT:\n{result.stdout}\n\n" f"STDERR:\n{result.stderr}" ) except Exception as e: return f"运行测试失败: {e}" from langchain.agents import create_agent from langchain.agents.middleware import TodoListMiddleware from langchain.messages import HumanMessage from rich import print as rprint # 1. 初始化 Agent agent = create_agent( model=llm, # write_todos 等工具,TodoListMiddleware 需要配合这些工具使用 tools=[list_files, read_file, write_file, run_tests], # 引入 Todo 列表中间件 middleware=[TodoListMiddleware()], system_prompt=( "你是一个代码修复助手。遇到多步骤任务时,先使用 write_todos 制定待办事项;" "然后读取文件、修复代码并运行测试。工作全部在工作区下进行。" ), ) # 2. 使用invoke进行同步调用 print("正在执行 Agent 任务...") final_state = agent.invoke( { "messages": [ HumanMessage(content="请测试并修复工作区下 my_add.py 文件中的代码") ] } ) rprint(final_state)

结果如下:

3. 其他内置中间件

3.1 ModelCallLimitMiddleware中间件

限制模型调用次数,避免无限循环,控制调用成本

案例:整个会话限制——优雅退出

案例:整个会话限制——抛异常

案例:单次调用限制——优雅退出

案例:单次调用限制——抛异常

3.2 ToolCallLimitMiddleware中间件

限制工具调用次数,可以限制所有工具调用的总次数,也可以限制特定工具的调用次数

退出行为有三种模式:

  • error:直接抛异常
  • end:结束整个会话
  • continue:继续运行 Agent,这是默认行为,此时 Agent 会将工具调用超出限制的信息传递给模型,后者自主决定后续行为,如果模型能力不足,可能导致死循环,为了避免这种情况,我们实现的 fake server 会以 20% 的概率输出正确响应,从而能终止循环

案例:整个会话限制——优雅结束

案例:整个会话限制——抛异常

案例:单次调用限制——继续运行

3.3 ModelFallbackMiddleware中间件

用于故障转移,当主模型无法访问时,启用备用模型

案例:模型初始化时定义备用模型

案例:创建 agent 时定义备用模型

3.4 LLMToolSelectorMiddleware中间件

智能工具筛选: 当工具太多时,用子模型筛选最相关的几个工具

参数:

  • model:用于工具筛选的子模型
  • max_tools:限定可以调用的工具总数
  • always_include:指定的工具不被计数

案例1:

案例2:

3.5 ToolRetryMiddleware中间件

基于指数退避算法,设置工具调用失败时的重试策略

3.6 ModelRetryMiddleware中间件

模型调用失败时重试,策略和工具调用的重试一样,都是基于指数退避算法

案例:继续运行

案例:抛异常

3.7 LLMToolEmulator中间件

某些情况下,工具尚未开发完成,我们希望先测试工具调用,可以用LLM tool emulator模拟工具

案例:

3.8 ContextEditingMiddleware中间件

上下文编辑中间件,该中间件提供了上下文管理的一种方式;通过更改发送给模型的消息列表来控制成本

案例:

3.9 FilesystemFileSearchMiddleware中间件

基于系统的 Glob 和 Grep 检索工具,为 Agent 赋予本地文件搜索和分析的能力:

  • Glob 根据文件路径检索
  • Grep 根据文件内容检索

3.10 Shell tool中间件

为 Agent 提供一个可以执行命令的 Shell 环境

3.11 Filesystem中间件

这是源自 deepagents(基于LangChain的另一个框架)的中间件,内置了四个工具,分别用于查看目录、读文件、写文件和改文件

4. 多个中间件组合及执行顺序

Middleware 可以叠加使用,其中中间件的书写顺序非常重要

案例:

5. 自定义中间件

某些复杂场景下,官方内置的中间件不能完全满足需求,此时可以通过实现 LangChain 暴露的中间件hook 函数构建自定义中间件

注意:尽量使用内置中间件

5.1 什么是 hook 函数(钩子函数)

hook 函数(钩子函数):在某个既定流程的特定时机,被框架、系统或主程序自动调用的扩展函数

核心特点:

  1. 不是主动在业务代码里随便调用的,而是当流程运行到某个“钩子点”时,系统自动触发
  2. 依附于一个更大的执行流程;比如“请求开始前”“模型调用前”“任务结束后”“异常发生时”等
  3. 作用是在不改主流程源码的前提下插入自己的逻辑。例如做日志 、 鉴权 、 修改输入 、 拦截输出 、 清理资源等

LangChain 的中间件作用在 Agent 架构中,后者是基于 LangGraph 构建的流程图

如下列出了六个hook 函数(钩子函数):

无论是官方内置中间件、自定义中间件、还是便捷装饰器中间件,通常都是通过实现其中的 一个或多个 hook 来生效的

5.2 LangChain 的 hook 函数分类

官方将六个钩子函数按照风格分为两类

类型1:Node-style hooks(节点风格钩子)

它们在流程的特定节点运行,适合顺序逻辑,如记录日志、验证

包括

  • before_agent:在Agent开始运行之前执行
  • before_model:在模型调用之前执行
  • after_model:在模型调用之后执行
  • after_agent:在Agent流程全部完成后执行

类型2:Wrap-style hooks(包装风格钩子)

它们在模型或工具调用前后运行,适合控制流,如重试、回退、缓存

包括

  • wrap_model_call (包裹模型调用)
  • wrap_tool_call (包裹工具调用)

5.3 Node-style hooks函数用法

支持两种用法

  • 装饰器是函数式挂载,把一个hook快速挂载到Agent的某个节点
  • 类写法是对象化中间件,把中间件封装为一个可配置、可复用、可扩展的组件

5.3.1 基本用法

1.基于装饰器实现

输出结果如下:

2.基于类实现

关键规则:

1.必须继承 AgentMiddleware← 这个固定

2.方法名固定 ( before_model , after_model )← 这个固定

3.类名随意← 这个不固定

案例:

输出如下:

1. before_model 通常的场景:

  • 消息修剪(trim messages)
  • PII 脱敏
  • 输入验证
  • 条件路由

2. after_model 通常的场景:

  • 输出验证
  • 格式化响应
  • 统计信息
  • 状态更新

5.3.2 两种方法的统一

装饰器是一种便捷的写法,其底层会基于我们重写的方法构造一个 AgentMiddleware 子类的实例

5.3.3 参数说明

Node-style hooks 函数有两个参数

  • state:是一个 AgentState 实例,维护 Agent 运行过程中的状态,这类状态会随着 Agent 的运行而发生变化,包括消息列表
  • runtime:是一个 Runtime 实例,维护 Agent 运行过程中的上下文环境,包括上下文 、 长期记忆等

5.3.4 返回值说明

返回 None:不修改状态(不修改Agent状态)

返回字典:更新状态

返回 {"jump_to": "..."}:控制流程

jump_to 目标:

  • "__end__" :结束 Agent
  • "tools" :跳到工具节点
  • 其他自定义节点

5.3.5 装饰器参数:can_jump_to

这里就涉及到 Node-style 的四个 hook 函数可以接收额外参数 can_jump_to

钩子函数可以改变 Agent 正常的运行轨迹 。比如:发现上下文窗口溢出,直接跳转至结尾,提前终止整个Agent

can_jump_to 决定了钩子函数可以直接跳转至流程的哪些位置,可取值如下:

  • end:跳转至 Agent 流程末尾,或第一个 after_agent 钩子,直接终止整个流程
  • tools:跳转至工具节点
  • model:跳转至模型节点,或第一个before_model钩子

1. 基于装饰器实现

2. 基于类实现

和基于装饰器实现的关键区别在于:需要引入额外的装饰器@hook_config为can_jump_to传参

5.4 Wrap-style hooks 函数用法

5.4.1 基本用法

1. wrap_model_call

① 基于装饰器实现

同时在模型调用前后做事,所以命名为 wrap_model_call ,wrap 意为包裹

案例:

② 基于类实现

2. wrap_tool_call

同时在工具调用前后做事,所以命名为 wrap_tool_call

① 基于装饰器实现

② 基于类实现

5.4.2 两种方式的统一

装饰器方法底层也会创建一个 AgentMiddleware 的实例

5.4.3 参数说明

  • request:被封装的请求对象,可以是模型或工具调用请求
  • handler:处理器,用于处理请求并返回调用结果

5.5 装饰器和类的选择

情况1:中间件只用一个钩子函数,推荐用装饰器,需要多个钩子函数推荐类写法

  • 当一个中间件只需要实现一个钩子函数时,直接使用装饰器最简单
  • 当一个中间件需要实现多个钩子函数时,类写法更合适

即:

  • 装饰器写法适合把单个 hook 快速挂到 agent 生命周期的某个节点上
  • 类写法更适合把多个 hook 组织为一个完整的中间件组件
  • 当中间件同时涉及 before_model 、 after_model 等多个钩子时,虽然装饰器工厂也能实现,但类写法在结构表达、配置归属、可维护性上更好

情况2:复杂配置推荐用类实现

装饰器当然也可以通过函数闭包传递参数,但在自省(运行时类型校验)、调试等方面天然不如类写法方便

情况3:跨项目复用推荐用类写法

如果希望中间件成为一个可实例化、可封装、可测试的组件,类写法更加合适,因为这些本就是类擅长 的场景,装饰器的闭包也能实现,但使用不友好

总结如下:

装饰器写法和类写法都能实现 middleware hook,本质上只是两种定义中间件的方式,并不是能力上完全割裂的两套机制,底层实现是统一的

一般来说:

  • 装饰器写法更适合单个 hook、逻辑简单、快速原型的场景
  • 类写法更适合多个 hook 组合、复杂配置、需要同时提供同步 / 异步实现、以及更强复用与可测试性的场景

5.6 hook函数执行顺序

  • before_* 钩子函数:从前到后执行
  • after_* 钩子函数:从后往前执行
  • wrap_* 钩子函数:洋葱架构,前面的包裹后面的,外层的先执行

这里的顺序并非定义顺序,而是创建Agent时传递中间件的顺序

案例:

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询