文档目录
前台路由系统

简介

本文件面向 DouPHP 前台路由系统,系统性说明 URL 解析、美化路由匹配、多语言前缀处理、中间件集成、请求预处理与响应后处理机制,并提供前台路由配置与自定义规则的实现方法。文档以代码级事实为依据,配合图示帮助读者快速理解并安全扩展。

项目结构

前台路由由“薄壳调度器 + 声明式匹配器 + 解析器 + 中间件”组成,入口仅负责剥离语言前缀并交给统一调度流程。

graph TB
A["前端请求"] --> B["LangPrefixParser<br/>剥离语言前缀"]
B --> C["Router::dispatch()<br/>调度入口"]
C --> D["FrontResolver::resolve()<br/>组装分发计划"]
D --> E["PrettyRouteMatcher::normalize()<br/>URL→控制器/动作/参数"]
D --> F["中间件链组装<br/>安全头/可信代理/限流/认证/CSRF"]
D --> G["Dispatcher::run()<br/>执行控制器"]
G --> H["Response/重定向/渲染"]

核心组件

  • LangPrefixParser:在请求早期从原始 route 串中识别并剥离语言前缀(如 zh-cn),返回语言标识与去前缀后的路由字符串,供后续流程使用。
  • PrettyRouteMatcher:基于 RouteManifest 的前台 declared 条目进行外观 URL 匹配;支持短地址策略、HTTP 方法消歧、占位符与字面段优先级排序,输出模块、动作、子段、控制器及具名参数。
  • FrontResolver:将匹配结果转换为 DispatchPlan,装配 Request 路由信息、视图变量、主题扩展加载,并按命中条目携带的中间件元数据组装中间件链。
  • Router:薄壳调度器,调用 FrontResolver 生成计划,交由 Dispatcher 执行;未命中时返回前台错误页。

架构总览

前台路由采用“声明式优先”的架构:所有可访问端点通过 front/route/*.php 与系统内置 manifest 声明,运行时由 PrettyRouteMatcher 编译正则并匹配,再由 FrontResolver 产出控制器与方法,最后经中间件链与安全策略执行。

sequenceDiagram
participant Client as "客户端"
participant Parser as "LangPrefixParser"
participant Router as "Router"
participant Resolver as "FrontResolver"
participant Matcher as "PrettyRouteMatcher"
participant MW as "中间件链"
participant Disp as "Dispatcher"
participant Ctrl as "控制器"
Client->>Parser : 原始 route 串
Parser-->>Client : {langSign, routeString}
Client->>Router : 进入 dispatch()
Router->>Resolver : resolve(request, container)
Resolver->>Matcher : normalize(routeString, method)
Matcher-->>Resolver : {matched, module, action, sub, params, controller, ...}
Resolver->>Resolver : 组装中间件链/设置Request/视图/主题扩展
Resolver-->>Router : DispatchPlan
Router->>Disp : run(plan, container)
Disp->>MW : 依次执行
MW->>Ctrl : 调用目标方法
Ctrl-->>Disp : Response/视图
Disp-->>Client : 响应

详细组件分析

LangPrefixParser:多语言前缀处理

  • 功能:识别形如 zh-cn 的语言前缀并剥离,返回语言标识与剩余路由。
  • 行为:若首段匹配语言码格式则视为语言前缀;否则语言标识为空串。
  • 影响:首页空串由后续逻辑派生;admin/api 不调用此解析器。
flowchart TD
Start(["输入 rawRoute"]) --> Trim["去除首尾斜杠并分割段"]
Trim --> CheckFirst{"第一段是否为语言前缀?"}
CheckFirst -- 是 --> Extract["提取 langSign 并移除该段"]
CheckFirst -- 否 --> Keep["保持原路由"]
Extract --> Join["拼接剩余段为 routeString"]
Keep --> Join
Join --> Return(["返回 {langSign, routeString}"])

PrettyRouteMatcher:美化路由匹配与 SEO 友好 URL

  • 入站匹配:仅依据前台命名空间的 declared 条目(系统内置与业务声明)。
  • 首页短路:空路由直接映射到 IndexController。
  • 短地址策略:启用时禁止带模块前缀的长格式;未命中时尝试补回模块前缀重试,保证可逆。
  • 匹配算法:收集全部命中规则 → 按“占位符少 > 字面段多 > 声明顺序”排序 → 传入 HTTP 方法进行方法感知过滤 → 首个合法者胜出。
  • 参数绑定:具名捕获组(除 module/action/sub_action)作为路由参数注入。
  • 出站兼容:与 PrettyUrlCompiler 共用编译引擎,确保 URL 生成与解析一致。
flowchart TD
S(["normalize(route, method)"]) --> Clean["清理首尾斜杠"]
Clean --> Home{"是否空串?"}
Home -- 是 --> HomeOut["返回 index/IndexController"]
Home -- 否 --> Prefixed{"是否带模块前缀且短地址启用?"}
Prefixed -- 是 --> Reject["拒绝长格式"]
Prefixed -- 否 --> Match["matchRoutePatterns 收集候选"]
Match --> Any{"有候选?"}
Any -- 否 --> ShortFallback{"短地址兜底? 补前缀再试"}
ShortFallback -- 命中 --> Build["buildResult 组装"]
ShortFallback -- 未命中 --> NotFound["返回未命中"]
Any -- 是 --> Sort["按特异性排序"]
Sort --> Method{"按 HTTP 方法过滤"}
Method --> Build
Build --> Out(["返回 {module,action,sub,params,controller,...}"])

FrontResolver:分发计划与中间件集成

  • 职责:读取 Request 的路由字符串与 HTTP 方法,委托 PrettyRouteMatcher 匹配;校验控制器存在性;根据配置与命中条目组装中间件链;设置 Request 路由信息与视图变量;加载主题扩展;返回 DispatchPlan。
  • 中间件默认栈:安全头 → 可信代理 → 限流 → (可选)会员认证 → CSRF。可通过命中条目的 mw_* 字段进行路由级细化(追加、排除、参数、跳过全部)。
  • 表单目标自动装配:对 create/edit 资源动作,自动计算 form_action 与 _method(POST/PUT),模板统一使用 {$form_action} 与 {$form_method}。
classDiagram
class FrontResolver {
+resolve(request, container) DispatchPlan
-composeMiddlewares(container, result) array
-assignFormTarget(action, result, params) void
-loadThemeExtension(container, module, action) void
}
class PrettyRouteMatcher {
+normalize(route, method) array
}
class MiddlewareRegistry {
+composeFromSpec(defaultAliases, skipAll, without, append, params) array
}
FrontResolver --> PrettyRouteMatcher : "匹配URL"
FrontResolver --> MiddlewareRegistry : "组装中间件链"

Router:调度入口与未命中处理

  • 职责:获取 Request,调用 FrontResolver 得到 DispatchPlan;若未命中则返回 page_wrong;否则交由 Dispatcher 执行;若控制器直接返回 Response,则透传。

中间件集成点与请求/响应处理

  • 前置处理:安全头、可信代理、限流、会员认证、CSRF。
  • 后置处理:由基类中间件在响应阶段写入安全头;CSRF 失败时抛出异常并返回提示并重定向;用户认证失败时跳转登录或返回 JSON 401(XHR)。
  • 路由级豁免:支付回调等可通过声明式 withoutMiddleware(['csrf']) 豁免 CSRF。

依赖关系分析

  • 入口依赖:Router 依赖 FrontResolver;FrontResolver 依赖 PrettyRouteMatcher 与中间件注册表。
  • 规则来源:PrettyRouteMatcher 依赖 RouteManifest 的前台 declared 条目;规则模式由 PrettyUrlCompiler 编译。
  • 配置驱动:config/route.php 提供风格化规则族,被 StyleRuleExpander 展开为具体 pattern,最终进入 declared 条目。
  • 模块隔离:仅 \Dou\Front 命名空间下的 declared 条目参与前台匹配,避免与 admin/api 冲突。
graph LR
R["config/route.php"] --> E["StyleRuleExpander(外部)"]
E --> M["RouteManifest(declared)"]
M --> P["PrettyRouteMatcher"]
P --> FR["FrontResolver"]
FR --> MW["中间件链"]
FR --> DP["Dispatcher"]

性能考量

  • 规则预编译:Pattern 正则仅在启动时编译一次,匹配阶段直接使用,降低重复开销。
  • 候选排序优化:先按占位符数量与字面段数量排序,减少正则回溯与分支判断。
  • 短地址兜底:仅在未命中时触发,避免额外成本。
  • 中间件按需挂载:默认栈固定,路由级细化通过 mw_* 字段精确控制,避免不必要中间件执行。
  • 建议:保持规则简洁明确,避免过度嵌套与复杂正则;合理使用短地址策略以减少 URL 长度与匹配复杂度。

故障排查指南

  • 未匹配(404):检查 URL 是否符合当前风格的 declared 规则;确认短地址策略是否启用;查看日志中的 route/lang/is_home 信息。
  • 控制器不存在:确认 declared 条目中的 controller FQCN 正确且类存在;检查模块开关(如 features.user)是否允许访问。
  • CSRF 失败:确认表单 token 是否正确生成与提交;检查一次性令牌路由映射;必要时通过路由级 withoutMiddleware(['csrf']) 豁免特定回调。
  • 认证失败:确认 auth('front') 已正确解析登录态;XHR 请求会返回 401 与跳转地址;普通请求将重定向至登录页。
  • 中间件问题:核对 mw_without/mw_append/mw_params 配置;确认中间件类存在且别名映射正确。

结论

DouPHP 前台路由系统以声明式为核心,通过 PrettyRouteMatcher 实现高确定性、高性能的外观 URL 匹配;FrontResolver 将匹配结果转化为可执行的控制器与方法,并统一装配中间件链与上下文;LangPrefixParser 提供轻量、无副作用的多语言前缀剥离;整体架构清晰、可扩展性强,便于在不同风格与短地址策略下保持一致的 SEO 友好 URL 体验。

附录:配置与扩展示例

前台路由配置示例

  • 栏目模块(column):通过 config/route.php 定义多种风格规则族,包含分类页、详情页、分页与短地址规则。
  • 简单模块(simple):提供 class、action/sub-action、id、列表等通用模式。
  • 单页面(page):支持 .html 后缀与无后缀两种形式。

自定义路由规则实现方法

  • 新增模块路由:在 front/route 下创建模块路由文件,使用 Route::column / simple / page / get / group 等方法声明;例如 product.php 与 article.php 分别声明 column 类型路由并绑定对应控制器。
  • 覆盖风格规则:复制 route.php 为 route_custom.php,仅覆盖需要的风格与规则项,无需复制全部。
  • 路由级中间件细化:在 declared 条目中通过 mw_without/mw_append/mw_params/skip_all_middleware 精细控制中间件链。
添加日期:2026-10-05