文档目录
路由中间件

简介

本技术文档聚焦 DouPHP 的路由中间件系统,围绕以下目标展开:

  • 解释中间件的执行管道机制:注册、排序与执行顺序。
  • 说明 RouteMiddlewareDsl 的声明式配置语法:条件中间件、参数化中间件与组合中间件。
  • 记录 RouteRules 的路由规则读取与选择机制(风格切换、短地址家族)。
  • 解释 StyleRuleExpander 如何将“风格规则”扩展为具体路由条目。
  • 提供自定义中间件开发指南:创建、注册、配置与调试。
  • 给出性能优化技巧与常见问题的排障方法。
  • 面向初学者解释中间件概念与使用场景;面向高级开发者提供复杂链设计模式。

项目结构

DouPHP 将“路由分发”和“中间件管线”解耦:

  • 路由分发器 Dispatcher 负责在命中路由后,把请求交给中间件管道执行,最终调用控制器动作。
  • 中间件管道 MiddlewarePipeline 以函数式组合方式串联多个中间件实例。
  • 中间件注册表 MiddlewareRegistry 负责将“全局默认别名栈 + 路由级细化”组装成最终可执行的中间件实例链,并支持参数化中间件注入。
  • 声明式 DSL RouteMiddlewareDsl 提供 fluent API,用于在路由声明中追加、豁免、覆盖或跳过中间件。
  • 路由规则 RouteRules 集中读取站点路由风格配置,供匹配与生成阶段复用。
  • 风格规则扩展 StyleRuleExpander 将“风格模板规则”展开为具体的 declared 路由条目。
graph TB
A["请求进入"] --> B["路由解析<br/>Dispatcher.run()"]
B --> C["构建中间件链<br/>MiddlewareRegistry.compose()"]
C --> D["执行管道<br/>MiddlewarePipeline.run()"]
D --> E["控制器动作<br/>容器调用"]
E --> F["返回响应"]

图示来源

  • Dispatcher.php:41-59
  • MiddlewareRegistry.php:66-139
  • MiddlewarePipeline.php:60-73

核心组件

  • 中间件管道 MiddlewarePipeline:按顺序组合中间件,最终调用控制器动作。
  • 中间件注册表 MiddlewareRegistry:合并默认栈与路由级细化,解析别名与参数,实例化中间件。
  • 参数化中间件接口 ParameterizedMiddleware:允许路由级参数覆盖,不污染共享默认实例。
  • 安全头基类 AbstractSecurityHeadersMiddleware:统一下发安全响应头。
  • 声明式 DSL RouteMiddlewareDsl:提供 middleware、withoutMiddleware、skipAllMiddleware、permission/throttle/auth 等便捷方法。
  • 路由规则 RouteRules:加载并缓存站点路由风格配置,供匹配与生成阶段使用。
  • 风格规则扩展 StyleRuleExpander:将风格模板规则展开为具体 declared 路由条目。

架构总览

下图展示了从请求到响应的完整流程,包括路由分发、中间件组装与执行、以及控制器调用。

sequenceDiagram
participant R as "客户端"
participant D as "Dispatcher"
participant MR as "MiddlewareRegistry"
participant MP as "MiddlewarePipeline"
participant M as "中间件链"
participant C as "控制器"
R->>D : 发起请求
D->>D : 设置路由参数到 Request
D->>MR : compose(默认别名栈, 路由条目)
MR-->>D : 返回已实例化的中间件数组
D->>MP : make(中间件数组)
MP->>M : 依次调用 handle(next)
M-->>C : 到达控制器动作
C-->>M : 返回结果
M-->>MP : 逐层返回
MP-->>D : 返回最终结果
D-->>R : 输出响应

图示来源

  • Dispatcher.php:41-59
  • MiddlewareRegistry.php:66-139
  • MiddlewarePipeline.php:60-73

详细组件分析

中间件管道 MiddlewarePipeline

  • 职责:将多个中间件实例组合为链式调用,最终执行控制器动作。
  • 执行顺序:内部以逆序折叠构造调用链,保证第一个注册的中间件最先执行。
  • 兼容性:兼容 PHP 5.6–8.5。
flowchart TD
Start(["开始"]) --> Build["逆序折叠构建调用链"]
Build --> CallFirst["调用首个中间件.handle(next)"]
CallFirst --> Next{"是否还有下一个?"}
Next --> |是| CallNext["调用下一个中间件.handle(next)"]
CallNext --> Next
Next --> |否| Controller["调用控制器动作"]
Controller --> Return["逐层返回结果"]
Return --> End(["结束"])

图示来源

  • MiddlewarePipeline.php:60-73

中间件注册表 MiddlewareRegistry

  • 职责:将“各端全局默认中间件栈(别名形态)”与“路由级细化”组装成最终可执行的中间件实例链。
  • 四种语义:
    • skipAllMiddleware:返回空链(最高优先级)。
    • withoutMiddleware:从默认栈过滤指定别名。
    • middleware:在默认栈末尾追加额外别名(支持 alias:p1,p2 参数 DSL)。
    • middlewareParams:给对应别名新建独占带参实例(不污染共享默认)。
  • 别名解析:parseSpec 支持 alias:p1,p2 形式;splitParams 拆分逗号分隔参数串。
  • 实例化:makeInstance 通过容器解析 FQCN,若实现 ParameterizedMiddleware 则注入路由级参数。
flowchart TD
S(["composeFromSpec"]) --> CheckSkip{"skipAll?"}
CheckSkip --> |是| Empty["返回空链"]
CheckSkip --> |否| Merge["合并默认栈与追加项<br/>去重保序"]
Merge --> ForEach["遍历规格 spec"]
ForEach --> Parse["解析别名与参数"]
Parse --> Make["容器实例化中间件"]
Make --> ParamCheck{"实现参数化接口?"}
ParamCheck --> |是| Inject["注入路由级参数"]
ParamCheck --> |否| SkipParam["跳过参数注入"]
Inject --> Add["加入实例列表"]
SkipParam --> Add
Add --> Next{"还有规格?"}
Next --> |是| ForEach
Next --> |否| Done["返回实例链"]

图示来源

  • MiddlewareRegistry.php:90-139
  • MiddlewareRegistry.php:151-168
  • MiddlewareRegistry.php:176-205

参数化中间件接口 ParameterizedMiddleware

  • 作用:支持“路由级参数覆盖”,当路由声明了对应别名参数时,为该路由新建独占实例并注入参数,避免污染共享默认实例。
  • 典型用法:权限节点、限流配额、鉴权模式等。

安全头中间件基类 AbstractSecurityHeadersMiddleware

  • 作用:在中间件最前置下发一组基线安全响应头(如 X-Content-Type-Options、X-Frame-Options、Referrer-Policy、Permissions-Policy),HSTS 仅在 HTTPS 且配置开启时下发。
  • 适用范围:仅对命中路由生效(未命中由 Router 自渲染 404,不在覆盖面内)。

声明式中间件 DSL RouteMiddlewareDsl

  • 提供 fluent API:
    • middleware([...]):追加中间件别名(支持 'alias:p1,p2' 参数形态)。
    • withoutMiddleware([...]):从默认栈中豁免指定别名。
    • skipAllMiddleware():跳过全部中间件(最高优先级)。
    • permission(node)、throttle(max, window)、auth(mode):参数糖,覆盖默认栈中对应中间件的参数。
  • 汇总字段:middlewareFields 将上述配置写入每条 RouteEntry。

路由规则 RouteRules

  • 职责:集中读取站点路由风格配置(合并 route.php 与 routecustom.php),按 site.route* 选中风格,供 UrlBuilder、PrettyRouteMatcher、RouteIdValidator 等共用。
  • 能力:
    • getSelectedRuleGroups:返回当前选中的规则分组(page/column/simple,含 column_short 短地址家族)。
    • getColumnDetailPattern:获取栏目详情 pattern。
    • columnDetailPatternUsesCategorySlug:判断详情 pattern 是否包含分类别名段。
  • 缓存:进程内缓存选中规则组,支持 clearCache 刷新。

风格规则扩展 StyleRuleExpander

  • 职责:将“风格模板规则”展开为具体的 declared 路由条目,替换 {module} 占位符,并按 target 派生 action。
  • 行为:
    • expandColumn:column 模块展开,短地址模块整族选用 short_rules。
    • expandSimple:simple 模块展开,保持与 PrettyRouteMatcher 一致的派生逻辑。
    • expandPage:page 模块展开,固定 module_fixed='page',action='show'。
  • 关键设计:不做 ModuleRegistry 准入校验;默认 controller FQCN 由调用方传入;风格切换在每次构建 manifest 时生效。

示例:API 端用户认证中间件 UserAuthMiddleware

  • 职责:API 端会员认证,继承抽象鉴权骨架,负责从 Authorization 头抽取 token,并调用 auth('api') guard 解析登录态;拒绝时直接发送 JSON 错误响应。
  • 配置文件路径:通过 configFile 返回 API 端中间件配置路径。

依赖关系分析

  • Dispatcher 依赖 MiddlewarePipeline 与 Container,负责在命中路由后将请求交给中间件管道执行。
  • MiddlewareRegistry 依赖 Container 与 RouteEntry,负责将别名映射为中间件实例,并处理参数注入。
  • RouteMiddlewareDsl 被路由构建器复用,产出中间件相关字段写入 RouteEntry。
  • RouteRules 与 StyleRuleExpander 配合,完成风格规则的读取与展开。
graph LR
D["Dispatcher"] --> P["MiddlewarePipeline"]
D --> MR["MiddlewareRegistry"]
MR --> C["Container"]
MR --> RE["RouteEntry"]
DSL["RouteMiddlewareDsl"] --> RE
RR["RouteRules"] --> SE["StyleRuleExpander"]

图示来源

  • Dispatcher.php:41-59
  • MiddlewareRegistry.php:66-139
  • RouteMiddlewareDsl.php:128-143
  • RouteRules.php:52-107
  • StyleRuleExpander.php:60-124

性能考量

  • 中间件链长度控制:尽量精简中间件数量,避免不必要的 I/O 或重型计算。
  • 参数化中间件隔离:通过 middlewareParams 为路由新建独占实例,避免共享状态污染与锁竞争。
  • 短路策略:尽早拒绝非法请求(如鉴权失败、限流触发),减少后续中间件与控制器开销。
  • 配置缓存:RouteRules 进程内缓存规则组,避免重复 include 与解析。
  • 安全头下发时机:在管道最前置下发,确保所有响应均携带必要安全头,同时避免重复设置。

故障排查指南

  • 中间件未生效:
    • 检查默认别名栈是否正确传入 MiddlewareRegistry。
    • 确认路由级 withoutMiddleware 是否误过滤了关键中间件。
    • 验证别名映射是否存在,缺失类会被跳过。
  • 参数未注入:
    • 确认中间件实现了 ParameterizedMiddleware 接口。
    • 检查 middlewareParams 或别名 DSL 参数格式是否正确。
  • 安全头未下发:
    • 确认请求是否命中路由(未命中由 Router 自渲染 404,不在覆盖面内)。
    • 检查 security.headers 配置与 HTTPS 判定。
  • 管道执行顺序异常:
    • 确认中间件注册顺序与追加顺序,注意 Pipeline 逆序折叠构造调用链。

结论

DouPHP 的路由中间件系统通过清晰的职责划分与灵活的 DSL,提供了强大的横切能力:

  • 管道机制保证了中间件的有序执行与可控短路。
  • 注册表支持别名映射、参数覆盖与豁免,满足复杂业务需求。
  • 声明式 DSL 降低了配置复杂度,提升可读性与可维护性。
  • 路由规则与风格扩展机制使多风格、多模块的路由管理更加统一。 建议在实际项目中遵循最小权限原则、合理组织中间件链,并结合监控与日志进行持续优化。

附录

中间件开发指南(自定义中间件)

  • 创建中间件:
    • 实现 MiddlewareInterface,编写 handle($next) 逻辑。
    • 如需路由级参数覆盖,实现 ParameterizedMiddleware 接口并提供 setRouteParameters(array $params)。
  • 注册别名:
    • 在各端 Resolver 构造时传入别名 → FQCN 映射(例如 admin: auth/permission/csrf/workspace;front/api: throttle/user_auth/csrf 等)。
  • 配置路由级细化:
    • 使用 RouteMiddlewareDsl 的 middleware、withoutMiddleware、skipAllMiddleware、permission/throttle/auth 等方法。
    • 或通过 RouteEntry 的 middleware、without_middleware、middleware_params、skip_all_middleware 字段。
  • 调试建议:
    • 在中间件入口与出口添加日志,记录请求 ID、耗时与关键状态。
    • 逐步缩小问题范围:先确认默认栈是否生效,再检查路由级追加与豁免。
    • 对于参数化中间件,打印注入的参数以确认 DSL 解析正确。

常用 DSL 速查

  • 追加中间件:middleware(['auth', 'throttle:5,60'])
  • 豁免中间件:withoutMiddleware(['csrf'])
  • 跳过全部中间件:skipAllMiddleware()
  • 参数糖:
    • permission('reports.sales_full')
    • throttle(10, 60)
    • auth('required')
添加日期:2026-10-05