简介
本文面向 DouPHP 框架的“中间件执行流程”,聚焦请求进入、中间件管道执行、响应返回的全生命周期。重点说明:
- 中间件的执行顺序控制、条件执行机制、短路执行模式
- 上下文对象在中间件间的传递与共享方式
- 不同路由类型(前台、后台、API)的中间件差异
- 执行流程图与时序图,帮助理解请求处理的生命周期
- 性能监控、执行时间统计与调试建议
项目结构
DouPHP 将中间件按端(前台 Front、后台 Admin、API)组织,并通过统一的调度器 Dispatcher 将匹配到的路由计划交由 MiddlewarePipeline 执行。安全头、鉴权、限流等横切关注点以中间件形式挂载到管道中。
graph TB
subgraph "调度层"
D["Dispatcher<br/>分发器"]
end
subgraph "中间件管道"
P["MiddlewarePipeline<br/>管道"]
M1["安全头中间件<br/>AbstractSecurityHeadersMiddleware"]
M2["认证中间件<br/>Admin/Front/API"]
M3["权限/限流/CSRF<br/>各端中间件"]
end
subgraph "控制器"
C["控制器方法<br/>业务逻辑"]
end
D --> P
P --> M1
M1 --> M2
M2 --> M3
M3 --> C
核心组件
- 中央分发器 Dispatcher:负责注入路由参数、构建并运行中间件管道,最终调用控制器方法。
- 路由条目 RouteEntry:承载路由元信息,包括是否跳过全部中间件、路由级中间件参数等。
- 安全头基类 AbstractSecurityHeadersMiddleware:在管道最前置下发安全响应头,仅对命中路由生效。
- 可参数化中间件接口 ParameterizedMiddleware:支持路由级参数覆盖,为每个路由创建独立实例并注入参数。
- 各端中间件:
- 后台 AuthMiddleware:恢复管理员登录态,未登录抛异常跳转登录页。
- 前台 UserAuthMiddleware:基于 Session 解析用户登录态,拒绝时重定向或返回 JSON。
- API UserAuthMiddleware:从 Authorization 头提取 token,解析用户上下文,拒绝时返回 JSON 错误。
架构总览
请求进入后,Dispatcher 将路由参数注入 Request,然后构造中间件管道。管道依次执行安全头、认证、权限/限流等中间件;任一中间件短路返回响应,后续中间件不再执行。若通过所有中间件,则实例化控制器并调用目标方法,返回值作为响应继续回传。
sequenceDiagram
participant Client as "客户端"
participant Router as "端路由器"
participant Disp as "Dispatcher"
participant Pipe as "MiddlewarePipeline"
participant MW1 as "安全头中间件"
participant MW2 as "认证中间件"
participant Ctrl as "控制器"
Client->>Router : HTTP 请求
Router-->>Disp : 分发计划(含中间件列表)
Disp->>Disp : 注入路由参数到 Request
Disp->>Pipe : 构建并运行管道
Pipe->>MW1 : handle(next)
MW1->>MW1 : 下发安全响应头
MW1->>Pipe : next()
Pipe->>MW2 : handle(next)
alt 认证失败
MW2-->>Pipe : 短路返回响应(JSON/重定向)
Pipe-->>Client : 响应
else 认证成功
MW2->>Pipe : next()
Pipe->>Ctrl : 调用控制器方法
Ctrl-->>Pipe : 返回响应
Pipe-->>Client : 响应
end
详细组件分析
分发器与管道执行
- Dispatcher 在管道前将路由参数写入 Request,确保中间件和控制器均可读取路径段参数。
- 使用 MiddlewarePipeline::make($plan->middlewares)->run(...) 包裹控制器调用,形成“洋葱模型”式执行链。
- 若计划标记为未找到(isNotFound),直接返回 null,由端路由器渲染专属 404。
flowchart TD
Start(["进入 Dispatcher.run"]) --> Check{"是否未匹配?"}
Check --> |是| ReturnNull["返回 null<br/>交由端路由渲染404"]
Check --> |否| Inject["注入路由参数到 Request"]
Inject --> BuildPipe["构建中间件管道"]
BuildPipe --> RunPipe["运行管道并调用控制器"]
RunPipe --> End(["返回响应"])
路由级中间件配置与短路
- RouteEntry 支持 skip_all_middleware 标志位,用于跳过该路由的全部中间件。
- 路由可声明 middleware_params,配合 ParameterizedMiddleware 实现参数化中间件(如 Throttle 次数/窗口、Permission 节点)。
- 中间件可在 handle 中提前返回响应,实现短路(例如认证失败、限流触发)。
flowchart TD
A["路由声明"] --> B{"skip_all_middleware ?"}
B --> |是| C["跳过全部中间件"]
B --> |否| D["加载中间件列表"]
D --> E{"是否存在路由级参数?"}
E --> |是| F["为参数化中间件新建实例并注入参数"]
E --> |否| G["使用默认实例"]
F --> H["执行管道"]
G --> H
H --> I{"中间件短路?"}
I --> |是| J["返回响应"]
I --> |否| K["继续下一个中间件"]
安全头中间件(全局前置)
- AbstractSecurityHeadersMiddleware 在管道最前置下发安全响应头(X-Content-Type-Options、X-Frame-Options、Referrer-Policy、Permissions-Policy),HSTS 仅在 HTTPS 且配置开启时下发。
- 仅对已匹配路由生效;404 由 Router 自渲染,不在覆盖范围内。
- 前台薄壳 SecurityHeadersMiddleware 继承基类,行为一致。
classDiagram
class AbstractSecurityHeadersMiddleware {
+handle(next) mixed
-sendHeaders(headers) void
}
class SecurityHeadersMiddleware_Front {
}
AbstractSecurityHeadersMiddleware <|-- SecurityHeadersMiddleware_Front
认证中间件(三端差异)
- 后台 AuthMiddleware:从 Session 恢复管理员登录态,未登录抛出 HttpResponseException 跳转登录页。
- 前台 UserAuthMiddleware:基于 auth('front') 解析登录态;XHR 请求 from=js 时返回 JSON 401 并附带跳转地址;否则重定向到登录页。
- API UserAuthMiddleware:从 Authorization: Bearer 提取 token,解析用户上下文;拒绝时返回 JSON 401/403。
sequenceDiagram
participant MW as "认证中间件"
participant Guard as "Guard(auth)"
participant Resp as "响应"
MW->>Guard : 解析登录态/上下文
alt 未认证
MW-->>Resp : 返回401/重定向
else 已认证
MW->>Guard : 注入身份缓存
MW-->>Resp : 放行next()
end
条件执行机制(模块级鉴权模式)
- 前台与 API 均提供 init/middleware.php 配置文件,定义 auth_modes 与 work_required。
- 规则键支持 module / module/action / module/sub / module/sub/action,值 public/optional/required,work_required 为 required 的子策略,额外校验工作端身份。
- 新增模块需显式登记,避免静默以匿名形态上线。
flowchart TD
R["当前路由段"] --> L["匹配 auth_modes 规则"]
L --> M{"模式? public/optional/required"}
M --> |public| P["不尝试解析登录态"]
M --> |optional| O["尝试解析登录态, 失败不拦截"]
M --> |required| Q["必须登录"]
Q --> W{"是否命中 work_required?"}
W --> |是| W1["额外校验工作端身份"]
W --> |否| N["放行"]
O --> N
P --> N
上下文对象的传递与共享
- 路由参数:Dispatcher 在管道前注入 Request,中间件与控制器均可通过 Request::route() 获取。
- 用户身份:认证中间件通过 Guard 解析并 hydrate 身份缓存,后续中间件与控制器可通过 auth('front'|'api'|'admin') 访问。
- 工作端身份:work_required 命中时,中间件会额外校验工作端身份,并在上下文中注入相关标识。
依赖关系分析
- Dispatcher 依赖 MiddlewarePipeline 与 Container,负责组装与执行。
- 各端中间件依赖各自 Guard(auth('front'|'api'|'admin'))与响应工具(ApiResponse、HttpResponseException)。
- 安全头中间件依赖配置中心 Config,读取 security.headers。
- 路由条目 RouteEntry 提供 skip_all_middleware 与 middleware_params,影响管道装配与参数注入。
graph LR
Dispatcher --> Pipeline["MiddlewarePipeline"]
Dispatcher --> Container["Container"]
Pipeline --> MW_Sec["安全头中间件"]
Pipeline --> MW_Auth["认证中间件"]
MW_Auth --> Guard["Guard(auth)"]
MW_Sec --> Config["Config"]
RouteEntry --> Pipeline
性能与监控
- 执行时间统计:建议在管道外层(Dispatcher 或端路由入口)记录开始时间,在控制器返回后计算耗时,便于定位慢请求。
- 中间件粒度日志:在每个中间件的 handle 前后记录日志,结合请求 ID 追踪链路。
- 短路优化:将高开销但可能短路的中间件(如认证、限流)置于靠前位置,减少不必要的后续处理。
- 路由级参数化:对限流、权限等中间件使用路由级参数,避免全局配置带来的误伤。
- 安全头最小化:仅在命中路由下发安全头,避免对静态资源或非命中路径造成额外开销。
故障排查指南
- 未命中路由:若计划标记为未找到,Dispatcher 返回 null,由端路由渲染 404;检查路由声明与匹配逻辑。
- 认证失败:
- 后台:抛出 HttpResponseException 跳转登录页,检查 Session 与 IP 绑定。
- 前台:XHR 请求 from=js 返回 JSON 401 并带 jump_url;普通请求重定向到登录页。
- API:返回 JSON 401/403,检查 Authorization 头与 token 有效性。
- 限流触发:确认 ThrottleMiddleware 的路由级参数(次数/窗口)是否合理。
- 安全头未下发:检查 headers_sent() 状态与 security.headers 配置。
结论
DouPHP 的中间件体系以 Dispatcher 为核心,结合 MiddlewarePipeline 形成清晰的请求处理流水线。安全头、认证、权限/限流等横切关注点通过中间件解耦,支持路由级参数化与条件执行。三端中间件在认证来源与响应格式上各有侧重,但整体执行模型一致。通过合理的中间件顺序、参数化配置与监控手段,可实现高效、可控的请求处理流程。
附录
- 关键概念速查:
- 中间件管道:洋葱模型,next() 控制流向,短路即返回响应。
- 路由级参数:通过 ParameterizedMiddleware 注入,避免污染共享实例。
- 条件执行:auth_modes 与 work_required 决定认证与工作端校验策略。
- 上下文共享:Request::route() 提供路由参数,auth() 提供用户与工作端身份。