文档目录
自定义中间件开发

简介

本指南面向 DouPHP 开发者,系统化说明如何基于框架的中间件机制进行自定义中间件开发。内容涵盖中间件的架构设计、生命周期、注册与执行顺序、管道机制、依赖注入与服务容器集成、配置与环境变量使用、性能优化与调试方法,以及测试策略与最佳实践。文档以现有代码为依据,提供可追溯的文件路径与图示,帮助快速上手并避免常见陷阱。

项目结构

DouPHP 将中间件按端(前台 front、后台 admin、接口 api)组织,并通过统一的中间件接口和抽象基类实现一致的管道行为。关键位置如下:

  • 核心接口与抽象基类:core/foundation/middleware
  • 后台中间件:admin/middleware
  • 接口中间件:api/middleware
  • 路由分发与计划:core/web/routing
  • API 鉴权模式配置:api/init/middleware.php
graph TB
subgraph "核心"
I["MiddlewareInterface"]
AUA["AbstractUserAuthMiddleware"]
ASH["AbstractSecurityHeadersMiddleware"]
end
subgraph "后台 Admin"
AM_Auth["AuthMiddleware"]
AM_Permission["PermissionMiddleware"]
AM_Csrf["CsrfMiddleware"]
end
subgraph "接口 Api"
AU_UserAuth["UserAuthMiddleware"]
AU_Throttle["ThrottleMiddleware"]
CFG["api/init/middleware.php"]
end
subgraph "路由"
D["Dispatcher"]
P["DispatchPlan"]
R["RouteEntry"]
end
I --> AM_Auth
I --> AM_Permission
I --> AM_Csrf
I --> AU_UserAuth
I --> AU_Throttle
AUA --> AU_UserAuth
ASH --> AU_UserAuth
D --> P
D --> R

核心组件

  • 中间件接口:定义 handle($next) 契约,要求“放行时必须 return $next()”,禁止丢弃下层返回值。
  • 用户认证抽象基类:封装 public/optional/required 三态决策、work 子策略、上下文解析与注入、拒绝响应等通用流程;子类只需实现 guard 选择、配置文件路径与拒绝逻辑。
  • 安全头抽象基类:统一在请求进入时下发安全响应头,仅对命中路由生效。
  • 具体中间件:
    • 后台:认证、权限、CSRF。
    • 接口:会员认证、限流。
  • 路由分发:Dispatcher 根据 DispatchPlan 执行控制器,并在中间件管道中运行。

架构总览

中间件在 HTTP 边界工作,位于路由匹配之后、控制器之前。请求进入 Dispatcher,由中间件管道依次调用各中间件的 handle,最终到达控制器或返回响应。API 端通过配置表集中声明鉴权模式,结合抽象基类的模板方法完成统一决策。

sequenceDiagram
participant C as "客户端"
participant R as "路由分发器 Dispatcher"
participant M1 as "安全头中间件"
participant M2 as "认证中间件"
participant M3 as "权限/限流中间件"
participant Ctrl as "控制器"
C->>R : "HTTP 请求"
R->>M1 : "handle(next)"
M1->>M2 : "return next()"
M2->>M3 : "return next()"
M3->>Ctrl : "return next()"
Ctrl-->>M3 : "Response"
M3-->>M2 : "Response"
M2-->>M1 : "Response"
M1-->>R : "Response"
R-->>C : "HTTP 响应"

详细组件分析

中间件接口与管道契约

  • 所有中间件必须实现 MiddlewareInterface::handle($next)。
  • 放行必须 return $next(),且不得丢弃其返回值,否则会导致控制器 Response 被吞掉。
  • 短路可通过抛出异常或直接发送响应后终止。

用户认证抽象基类(模板方法)

  • 负责从 Request 提取 module/action/sub/parent,结合配置表与路由级覆盖决定鉴权模式。
  • 支持 public/optional/required 三态与 work_required 子策略。
  • 子类需实现:configFile()、resolveContext()、inject()、hasWorkIdentity()、rejectUnauthenticated()、rejectForbidden()。
  • 典型用法:API 端 UserAuthMiddleware 继承该基类,实现 guard 选择与 JSON 拒绝。
classDiagram
class MiddlewareInterface {
+handle(next) mixed
}
class AbstractUserAuthMiddleware {
-authModes array
-workRequired array
-routeModeOverride string?
+setRouteParameters(params) void
+handle(next) mixed
#configFile() string
#resolveContext() array
#inject(context) void
#hasWorkIdentity() bool
#rejectUnauthenticated() void
#rejectForbidden() void
}
class UserAuthMiddleware {
+configFile() string
+resolveContext() array
+inject(context) void
+hasWorkIdentity() bool
+rejectUnauthenticated() void
+rejectForbidden() void
}
MiddlewareInterface <|.. AbstractUserAuthMiddleware
AbstractUserAuthMiddleware <|-- UserAuthMiddleware

后台认证与权限中间件

  • AuthMiddleware:恢复管理员会话,未登录抛异常跳转登录页。
  • PermissionMiddleware:校验当前管理员对模块/动作的访问权限,依赖 AdminGate。
  • CsrfMiddleware:校验表单令牌,处理特殊场景与失败提示。
flowchart TD
Start(["进入后台中间件"]) --> Auth["恢复管理员会话"]
Auth --> |未登录| Redirect["跳转登录页"]
Auth --> |已登录| Perm["检查模块/动作权限"]
Perm --> |无权限| Redirect
Perm --> |有权限| Next["继续管道/控制器"]

接口限流中间件

  • ThrottleMiddleware:按 IP 对敏感写接口与公共匿名写接口限流,超限返回 429 并设置 Retry-After。
  • 限流规则集中在类内映射,按 module/action/sub 候选键匹配。
flowchart TD
S(["进入限流中间件"]) --> Match{"是否命中限流规则?"}
Match --> |否| Next["继续管道"]
Match --> |是| Check["统计窗口内次数"]
Check --> |未超限| Next
Check --> |超限| Reject["返回 429 并 exit"]

API 鉴权模式配置

  • api/init/middleware.php 集中声明 auth_modes 与 work_required。
  • 支持 module / module/action / module/sub / module/sub/action 四级覆盖。
  • 新增模块必须在 auth_modes 显式登记,防止静默上线为匿名可访问。

路由与中间件执行点

  • Dispatcher 接收 DispatchPlan,仅在命中路由时运行中间件管道。
  • RouteEntry 记录路由元信息(如 skip_all_middleware),用于控制中间件执行范围。

依赖关系分析

  • 中间件对框架服务(auth、request、config)的依赖通过全局辅助函数或 Guard 抽象,保持低耦合。
  • 抽象基类屏蔽了不同端的差异,子类仅需关注端特有逻辑。
  • 配置驱动:API 鉴权模式通过配置文件集中管理,便于审计与扫描。
graph LR
UI["业务控制器"] --> MW["中间件管道"]
MW --> AUTH["认证/权限/限流"]
AUTH --> CFG["鉴权模式配置"]
MW --> REQ["Request/Response"]
MW --> CONF["Config"]

性能考虑

  • 尽早短路:认证失败、限流超限应尽快返回,避免后续昂贵操作。
  • 减少 I/O:在中间件中避免频繁数据库查询,必要时缓存结果。
  • 合理排序:将轻量且通用的中间件(如安全头、限流)置于管道前端,提高命中率与整体吞吐。
  • 避免重复计算:复用 Request 中的路由信息,减少解析开销。
  • 日志采样:在高并发场景下对日志写入进行采样或异步化。

故障排查指南

  • 响应被吞掉:检查是否在 handle 中调用了 $next() 却未 return 其返回值。
  • 鉴权误拦截:核对 api/init/middleware.php 中对应模块/动作的 auth_modes 是否正确。
  • CSRF 失败:确认表单是否携带正确令牌,以及路由豁免列表是否包含目标接口。
  • 限流误判:检查 throttleFor 候选键是否与 module/action/sub 匹配一致。
  • 权限拒绝:确认 AdminGate 判定逻辑与当前管理员角色/动作白名单。

结论

DouPHP 的中间件体系通过统一接口与抽象基类,实现了跨端一致的管道模型与可扩展的鉴权、限流、安全头等横切能力。开发者只需遵循 handle 契约、利用模板方法扩展端特有逻辑,并通过配置文件集中管理鉴权策略,即可高效构建可维护、可测试、高性能的中间件。

附录:模板与示例

自定义中间件模板(最小实现)

  • 实现 MiddlewareInterface::handle($next),确保放行时 return $next()。
  • 如需鉴权三态,建议继承 AbstractUserAuthMiddleware 并实现必要抽象方法。
  • 如需下发安全头,可继承 AbstractSecurityHeadersMiddleware。

实际应用场景示例(基于现有实现)

  • 日志记录:可在任意中间件 handle 前后记录请求/响应耗时与关键参数,注意采样与异步。
  • 数据预处理:在认证前对输入做标准化清洗,例如去除多余空白、规范化编码。
  • 响应修改:在 $next() 返回后包装响应,添加统一字段或追踪 ID。
  • 权限检查:参考 PermissionMiddleware,基于 AdminGate 或业务 Gate 进行细粒度授权。

依赖注入与服务容器集成

  • 中间件可通过构造函数注入服务(如 PermissionMiddleware 注入 AdminGate)。
  • 容器会在管道中自动解析依赖,无需手动 new。

配置管理与环境变量使用

  • API 鉴权模式集中于 api/init/middleware.php,按模块/动作/子段覆盖。
  • 安全头通过 Config::get('security.headers') 读取,可按环境切换。

调试方法与测试建议

  • 断点调试:在 handle 入口与关键分支处设置断点,观察 $next 返回值。
  • 日志定位:在中间件前后输出请求标识与耗时,配合采样降低开销。
  • 单元测试:
    • 构造 Mock Request/Response,验证 handle 在不同输入下的行为。
    • 针对鉴权三态与 work_required 子策略编写用例,覆盖 public/optional/required 分支。
    • 针对限流中间件模拟多次请求,验证阈值与重试头。
添加日期:2026-10-05