简介
本指南面向 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 分支。
- 针对限流中间件模拟多次请求,验证阈值与重试头。