简介
本文件面向 DouPHP 框架的中间件系统,系统性说明管道模式的设计原理、中间件的注册与执行顺序控制、请求在管道中的流转过程、上下文对象生命周期管理,以及前台、后台、API 三端中间件配置的差异。文档同时给出中间件容器实现要点、依赖注入机制与错误处理策略,并配套架构图与执行流程图,帮助开发者快速理解与扩展中间件能力。
项目结构
DouPHP 将中间件能力下沉到核心层,并在各应用端(前台 front、后台 admin、API api)通过各自的 Resolver 装配默认中间件栈,最终由中央分发器 Dispatcher 统一执行管道。
graph TB
subgraph "核心层"
MI["中间件接口<br/>MiddlewareInterface"]
PIPE["管道执行器<br/>MiddlewarePipeline"]
DISP["分发器<br/>Dispatcher"]
REQ["请求对象<br/>Request"]
end
subgraph "后台端"
A_RES["后台解析器<br/>AdminResolver"]
A_MW["后台中间件集合<br/>安全头/代理信任/认证/权限/CSRF/工作台"]
end
subgraph "API端"
API_RES["API解析器<br/>ApiResolver"]
API_MW["API中间件集合<br/>安全头/代理信任/限流/用户认证"]
end
subgraph "前台端"
F_MW["前台中间件<br/>CSRF等"]
end
A_RES --> DISP
API_RES --> DISP
DISP --> PIPE
PIPE --> A_MW
PIPE --> API_MW
PIPE --> F_MW
DISP --> REQ
核心组件
- 中间件接口:定义统一的 handle($next) 契约,要求中间件必须调用 $next() 并将下层返回值原样冒泡,禁止吞掉控制器响应。
- 安全头基类:提供按配置下发安全响应头的通用逻辑,三端可继承以保持一致行为。
- 分发器:负责在中间件管道之前注入路由参数,并在管道中懒实例化控制器并调用方法。
- 解析器:各端 Resolver 根据声明式路由命中结果,组装默认中间件别名栈,并通过 MiddlewareRegistry 生成最终中间件链。
- 端侧中间件:后台 Auth/Permission/CSRF/Workspace;API Throttle/UserAuth;前台 CSRF 等。
架构总览
下图展示从请求进入、路由解析、中间件管道到控制器执行的完整流程。
sequenceDiagram
participant C as "客户端"
participant R as "端解析器<br/>AdminResolver/ApiResolver"
participant D as "分发器<br/>Dispatcher"
participant P as "管道执行器<br/>MiddlewarePipeline"
participant M as "中间件链"
participant Ctrl as "控制器"
C->>R : 发起HTTP请求
R-->>D : 返回DispatchPlan(含中间件别名栈)
D->>D : 注入路由参数至Request
D->>P : 创建管道并传入控制器闭包
P->>M : 依次执行中间件.handle(next)
M-->>P : 放行或短路返回
P->>Ctrl : 调用控制器方法
Ctrl-->>P : 返回Response/数据
P-->>D : 冒泡上层返回值
D-->>C : 输出响应
详细组件分析
管道模式与执行顺序控制
- 管道入口:Dispatcher.run 接收 DispatchPlan,先写入 Request 的路由参数,再使用 MiddlewarePipeline::make($plan->middlewares)->run(...) 构建管道并执行。
- 执行顺序:由各端 Resolver 的默认别名栈决定,例如后台默认栈为“安全头 → 代理信任 → 认证 → 权限 → CSRF → 工作台”,API 默认栈为“安全头 → 代理信任 → 限流 → 用户认证(可选)”。
- 短路机制:中间件可在 handle 中直接返回响应或抛出异常,从而阻断后续链路;否则必须 return $next() 保证下层返回值冒泡。
flowchart TD
Start(["进入管道"]) --> Next1["中间件1.handle(next)"]
Next1 --> Check1{"是否放行?"}
Check1 -- 否 --> Return1["返回响应/抛异常"]
Check1 -- 是 --> Next2["中间件2.handle(next)"]
Next2 --> Check2{"是否放行?"}
Check2 -- 否 --> Return2["返回响应/抛异常"]
Check2 -- 是 --> Controller["控制器调用"]
Controller --> Bubble["返回值逐层冒泡"]
Bubble --> End(["结束"])
中间件注册机制
- 别名映射:各端 Resolver 维护 aliasMap,将字符串别名映射到具体中间件类名。
- 默认栈:Resolver 定义默认别名数组,作为“安全默认”的基础栈。
- 组合规则:通过 MiddlewareRegistry::compose(默认栈, 命中条目携带的中继配置) 叠加路由级细化,得到最终中间件实例链。
不同应用层的中间件配置差异
- 后台(Admin)
- 默认栈包含:安全头、代理信任、认证、权限、CSRF、工作台。
- 典型中间件:
- 认证:从会话恢复管理员登录态,未登录跳转登录页。
- 权限:基于工作区与角色进行模块准入校验。
- CSRF:表单提交保护。
- 工作台:注入全局视图变量。
- API
- 默认栈包含:安全头、代理信任、限流;当 features.user 开启时追加用户认证。
- 典型中间件:
- 限流:限制请求频率。
- 用户认证:从 Authorization: Bearer 提取 token,解析登录态并注入上下文;拒绝时返回 JSON 错误。
- 前台(Front)
- 典型中间件:CSRF,支持一次性令牌与静态令牌模型,GET 特定路由也校验。
请求在管道中的流转与上下文生命周期
- 路由参数注入:Dispatcher 在管道前将路由参数写入 Request,确保中间件与控制器均可通过 Request::route() 获取路径段参数。
- 上下文对象:
- 后台:认证后通过会话恢复管理员信息,权限与工作区上下文在后续中间件注入。
- API:认证中间件从请求头解析 token,调用 auth('api') 解析用户上下文并 hydrate 到 guard,供后续控制器使用。
- 生命周期:中间件在 handle 内完成前置处理(鉴权、限流、安全头等),调用 $next() 后继续后置处理(如记录日志、附加响应头)。
中间件容器的实现细节与依赖注入
- 容器职责:Dispatcher 通过 Container::make 懒实例化控制器,并通过 Container::call 调用方法,传递场景参数 __scene。
- 依赖注入:中间件与控制器均通过容器解析依赖,Resolver 仅负责组装中间件别名链,不直接耦合具体实现。
- Guard 注册:
- 后台 Init 注册 admin guard。
- API Init 先以 GuestGuard 兜底,若 user 模块可用则替换为真实 Auth,并由 UserAuthMiddleware 在运行时注入上下文。
错误处理策略
- 后台认证失败:抛出 HttpResponseException 并重定向到登录页。
- API 认证失败:直接发送 JSON 错误响应(401/403)并终止。
- 前台 CSRF 失败:抛出 DomainException 并提示页面过期,引导刷新或重新登录。
- 安全头基类:仅在 headers 未发送且配置存在时下发,避免重复或无效设置。
依赖关系分析
- 低耦合:Resolver 只负责别名到类的映射与默认栈组合;Dispatcher 只负责管道编排与控制器调用;中间件通过接口契约协作。
- 关键依赖链:
- AdminResolver/ApiResolver → MiddlewareRegistry → 中间件链
- Dispatcher → MiddlewarePipeline → 控制器
- 各端 Init → Guard 注册 → 中间件运行时使用
classDiagram
class MiddlewareInterface {
+handle(next) mixed
}
class AbstractSecurityHeadersMiddleware {
+handle(next) mixed
}
class AdminResolver {
-defaultAliases : string[]
-aliasMap : map
+resolve(request, container) DispatchPlan
}
class ApiResolver {
-defaultAliases() : string[]
-aliasMap : map
+resolve(request, container) DispatchPlan
}
class Dispatcher {
+run(plan, container) mixed
}
class AuthMiddleware
class UserAuthMiddleware
class CsrfMiddleware
AbstractSecurityHeadersMiddleware ..|> MiddlewareInterface
AuthMiddleware ..|> MiddlewareInterface
UserAuthMiddleware ..|> MiddlewareInterface
CsrfMiddleware ..|> MiddlewareInterface
AdminResolver --> Dispatcher : "产出DispatchPlan"
ApiResolver --> Dispatcher : "产出DispatchPlan"
Dispatcher --> MiddlewareInterface : "执行管道"
性能考量
- 懒实例化:控制器在管道内部按需实例化,减少无谓开销。
- 最小化头部操作:安全头中间件仅在 headers 未发送时设置,避免重复与性能损耗。
- 条件启用:API 用户认证仅在 features.user 开启时加入默认栈,降低无关开销。
- 路由参数提前注入:在管道前写入 Request,避免中间件重复解析。
故障排查指南
- 认证失败
- 后台:检查会话恢复逻辑与重定向目标是否正确。
- API:检查 Authorization 头与 token 解析、guard 注入是否成功。
- CSRF 校验失败
- 前台:确认表单 token 生成与匹配逻辑,关注一次性令牌路由映射与 GET 校验列表。
- 安全头未生效
- 检查配置项 security.headers 是否存在,以及是否在 headers 已发送后尝试设置。
- 中间件顺序问题
- 核对 Resolver 默认栈顺序与路由级覆盖,确保鉴权在业务逻辑之前、限流在认证之前等。
结论
DouPHP 的中间件系统以清晰的接口契约与分层 Resolver 为核心,结合 Dispatcher 的统一管道编排,实现了跨前台、后台、API 的一致处理能力。通过别名映射与默认栈组合,既保证了“安全默认”,又允许路由级精细化定制。配合容器依赖注入与 Guard 机制,开发者可以便捷地扩展鉴权、限流、安全头等横切能力,并以可控的错误处理策略保障系统稳定性。
附录
- 最佳实践建议
- 始终 return $next() 以保证响应冒泡。
- 将横切关注点拆分为单一职责中间件,便于复用与测试。
- 使用 Resolver 的 withoutMiddleware 声明式豁免敏感路由,避免硬编码白名单。
- 在 API 端优先限流与认证,再进入业务逻辑。