文档目录
前台路由解析器

简介

本文件面向 DouPHP 框架的前台路由解析器,系统性阐述 FrontResolver、PrettyRouteMatcher、LangPrefixParser 与 Router 的设计原理与实现机制,覆盖 URL 模式匹配、控制器解析、参数提取、中间件链组装、多语言前缀处理、表单目标 URL 自动装配、主题扩展加载等关键能力。文档同时提供配置示例、性能优化建议、调试方法与常见问题解决方案,帮助开发者快速理解并高效使用前台路由系统。

项目结构

前台路由相关代码集中在 front/foundation/routing 目录下,配合 front/middleware 与 front/route 声明式路由文件共同构成完整的前台请求分发链路:

  • LangPrefixParser:在入口阶段剥离多语言前缀,将语言标识与去前缀后的路由串分别写入 Request。
  • PrettyRouteMatcher:基于 RouteManifest 的 declared 条目进行外观 URL 匹配,输出标准化路由信息(模块、动作、子段、控制器、参数、中间件细化)。
  • FrontResolver:以 PrettyRouteMatcher 为唯一外观 URL 解析来源,组装中间件链、设置视图上下文、加载主题扩展,产出 DispatchPlan。
  • Router:薄壳调度器,调用 FrontResolver 生成计划后交由中央 Dispatcher 执行;未命中时返回 404 提示。
graph TB
A["前端请求"] --> B["LangPrefixParser<br/>剥离语言前缀"]
B --> C["Router::dispatch()"]
C --> D["FrontResolver::resolve()"]
D --> E["PrettyRouteMatcher::normalize()"]
E --> F["RouteManifest<br/>declared 条目"]
D --> G["中间件链组装<br/>composeMiddlewares()"]
D --> H["主题扩展加载<br/>ThemeExtensionLoader"]
D --> I["DispatchPlan<br/>控制器/方法/参数/中间件"]
C --> J["Dispatcher::run()"]
J --> K["响应"]

核心组件

  • LangPrefixParser:纯函数级工具,仅根据首段是否符合语言代码格式剥离语言前缀,返回语言标识与去前缀路由串,供后续初始化流程消费。
  • PrettyRouteMatcher:负责将外观 URL 转换为标准路由信息,支持短地址策略、方法感知消歧、优先级排序与具名参数提取。
  • FrontResolver:统一入口,完成控制器解析、参数注入、视图赋值、主题扩展加载与中间件链组装,最终产出 DispatchPlan。
  • Router:薄壳调度器,协调 FrontResolver 与中央 Dispatcher,处理未命中时的 404 响应。

架构总览

前台请求进入后,先由 LangPrefixParser 剥离语言前缀,再由 Router 委托 FrontResolver 解析为 DispatchPlan。FrontResolver 内部通过 PrettyRouteMatcher 从 RouteManifest 的 declared 条目中匹配规则,得到模块、动作、子段、控制器与参数,并依据命中条目的中间件细化字段组装中间件链。随后设置请求基址、路由信息与视图变量,加载主题扩展,最后交由 Dispatcher 执行控制器方法。

sequenceDiagram
participant Client as "客户端"
participant Router as "Router"
participant Resolver as "FrontResolver"
participant Matcher as "PrettyRouteMatcher"
participant MW as "中间件链"
participant Disp as "Dispatcher"
Client->>Router : 发起请求
Router->>Resolver : resolve(request, container)
Resolver->>Matcher : normalize(route, method)
Matcher-->>Resolver : {matched, module, action, sub, controller, params, mw_*}
alt 未匹配
Resolver-->>Router : notFound
Router-->>Client : 404 page_wrong
else 已匹配
Resolver->>Resolver : composeMiddlewares(result)
Resolver->>Resolver : assignFormTarget / loadThemeExtension
Resolver-->>Router : DispatchPlan
Router->>Disp : run(plan, container)
Disp-->>Client : Response
end

详细组件分析

FrontResolver 设计原理与实现

  • 职责边界:接收 Request 与 Container,调用 PrettyRouteMatcher 获取标准化路由信息;若未命中则记录日志并返回 notFound;命中后校验控制器类存在性,必要时断言会员模块可用性。
  • 中间件链组装:默认栈顺序为安全头 -> 可信代理 -> 定向限流 -> [可选会员认证] -> CSRF;支持路由级豁免(mw_without)、追加(mw_append)、参数(mw_params)与跳过全部(mw_skip_all)。
  • 视图与主题集成:设置 base_url、route/module/action/sub、合并路由参数到输入、分配当前模块 cur;针对 create/edit 动作自动装配表单目标 URL 与方法伪装值;在控制器执行前加载主题扩展 inc/..from_theme.php。
  • 分发计划:构造 DispatchPlan,包含控制器 FQCN、方法解析结果、参数与中间件链。
flowchart TD
Start(["入口 resolve"]) --> GetRoute["读取 routeString 与方法"]
GetRoute --> Normalize["PrettyRouteMatcher.normalize()"]
Normalize --> Matched{"是否匹配?"}
Matched -- 否 --> LogWarn["记录未匹配日志"] --> NotFound["返回 notFound"]
Matched -- 是 --> Extract["提取 module/action/sub/params/controller"]
Extract --> Validate{"controller 有效?"}
Validate -- 否 --> LogWarn2["记录失败日志"] --> NotFound
Validate -- 是 --> ModuleCheck["Module::assertUserAvailable()"]
ModuleCheck --> ComposeMW["composeMiddlewares()"]
ComposeMW --> SetCtx["设置 base_url/route/params/cur"]
SetCtx --> FormTarget{"create/edit ?"}
FormTarget -- 是 --> AssignForm["assignFormTarget()"]
FormTarget -- 否 --> ThemeLoad["loadThemeExtension()"]
AssignForm --> ThemeLoad
ThemeLoad --> Plan["new DispatchPlan(...)"]
Plan --> End(["返回计划"])

PrettyRouteMatcher 工作原理

  • 入站匹配范围:仅基于 RouteManifest 的 declared 条目(系统内置端点与业务端点),pattern 正则编译统一交给 PrettyUrlCompiler,保证与出站 UrlBuilder 双向可逆。
  • 首页短路:空字符串直接返回 IndexController,避免空 pattern 参与通用正则匹配的歧义。
  • 短地址策略:启用后禁止「模块名/…」长格式;原路径未命中时补回模块前缀再用同一套 declared 规则重试,确保短链与完整模板可逆。
  • 匹配算法:收集全部命中规则,按 specificity 排序(占位符少优先、字面段多优先、声明顺序兜底),再按 HTTP 方法过滤首个命中;未传方法时返回首条(permissive)。
  • 结果构建:从命中规则的显式字段提取 module/target/action/sub/controller/name,并将具名捕获组作为 params 带出;同时携带 mw_* 路由级细化字段。
flowchart TD
NStart["normalize(route, method)"] --> Trim["trim('/')"]
Trim --> Blank{"是否为空?"}
Blank -- 是 --> Home["返回 IndexController"]
Blank -- 否 --> ShortCheck{"ShortUrlPolicy.isPrefixedRoute?"}
ShortCheck -- 是 --> Reject["返回空白结果"]
ShortCheck -- 否 --> Match["matchRoutePatterns()"]
Match --> Found{"有命中?"}
Found -- 否 --> ShortFallback{"ShortUrlPolicy.enabled()?"}
ShortFallback -- 是 --> Prefix["prefixRoute(route) 重试"]
Prefix --> Found2{"重试命中?"}
Found2 -- 是 --> Build["buildResult()"]
Found2 -- 否 --> ReturnBlank["返回空白结果"]
Found -- 是 --> Build
Build --> NEnd["返回标准化路由信息"]

LangPrefixParser 多语言前缀处理

  • 行为:对原始 route 串检查首段是否符合语言代码格式(如 zh-cn),若匹配则剥离并返回 langSign 与去前缀后的 routeString。
  • 安全性:纯函数级工具,不依赖 Config/Locale/容器,可在 Init 之前安全运行;admin/api 无 URL 语言前缀,不调用本类。
  • 集成:由前台入口在 Init 之前调用,将语言前缀与去前缀路由串分别写入 Request,供后续初始化流程消费。

中间件链组装过程

  • 默认栈顺序:安全头 -> 可信代理 -> 定向限流 -> [可选会员认证] -> CSRF。
  • 条件入栈:当 features.user 开启时加入 user_auth;若中间件类缺失则由 MiddlewareRegistry 吞掉跳过,不致命。
  • 路由级细化:命中条目的 mw_without、mw_append、mw_params、mw_skip_all 字段经 registry.composeFromSpec 组合,形成最终中间件链。
  • 典型中间件:
    • SecurityHeadersMiddleware:基线安全响应头。
    • CsrfMiddleware:前台令牌模型(登录会员共享静态令牌;匿名表单一次性令牌);外部支付回调可通过路由级 withoutMiddleware(['csrf']) 豁免。
    • UserAuthMiddleware:走 auth('front') 解析登录态、注入身份缓存;拒绝时跳转登录页或返回 JSON 401。

前台路由配置示例

  • 用户模块(user.php):使用 group/prefix/sub/name/get/post/resource 等声明式 API,定义公开登录入口与会员中心资源。
  • 产品模块(product.php):使用 column 风格展开为列表/分类/详情等 declared 条目,控制器为 ProductController。

前台路由与主题系统集成

  • 主题扩展加载:FrontResolver 在控制器执行前调用 ThemeExtensionLoader,按 routeModule/routeAction 加载主题包 inc/..from_theme.php,便于主题层对特定页面进行增强。
  • 视图上下文:设置 cur 为当前模块,便于模板渲染;create/edit 动作自动装配 form_action/form_method,模板统一使用 {$form_action} 与 {$form_method}。

表单目标 URL 自动装配机制

  • 触发条件:仅当命中动作为 create 或 edit,且路由名为资源型(含点号分隔的 base.action)。
  • 装配逻辑:
    • create:form_action 指向 base.store(POST),form_method 为空(等价原生 POST)。
    • edit:form_action 指向 base.update(PUT),form_method 为 PUT;URL 中附带成员 id(来自具名参数)。
  • 模板使用:{$form_action} 与 {$form_method} 统一渲染表单提交目标与方法伪装。

依赖关系分析

  • Router 依赖 FrontResolver 与 Dispatcher。
  • FrontResolver 依赖 PrettyRouteMatcher、MiddlewareRegistry、ThemeExtensionLoader、View、Module、MethodResolver。
  • PrettyRouteMatcher 依赖 RouteManifest、PrettyUrlCompiler、ShortUrlPolicy。
  • 中间件依赖各自抽象基类与全局配置(features.user)。
classDiagram
class Router {
+dispatch() Response|null
}
class FrontResolver {
+resolve(Request, Container) DispatchPlan
-composeMiddlewares(Container, array) array
-assignFormTarget(string, array, array) void
-loadThemeExtension(Container, string, string) void
}
class PrettyRouteMatcher {
+normalize(string, string|null) array
-matchRoutePatterns(string, string|null) array|null
-buildResult(array, array) array
-loadRoutePatterns() array
}
class LangPrefixParser {
+parse(string) array
}
class MiddlewareRegistry {
+composeFromSpec(array, bool, array, array, array) array
}
class ThemeExtensionLoader {
+loadForRoute(string, string) void
}
Router --> FrontResolver : "委托解析"
FrontResolver --> PrettyRouteMatcher : "匹配规则"
FrontResolver --> MiddlewareRegistry : "组装中间件"
FrontResolver --> ThemeExtensionLoader : "加载主题扩展"
Router --> Dispatcher : "执行计划"

性能考虑

  • 预编译正则:PrettyRouteMatcher 在构造时加载并预编译所有 declared 规则的 _regex,减少运行时正则编译开销。
  • 优先级排序:specificity 排序仅在候选集合上进行,避免全量扫描;方法感知过滤进一步缩小匹配范围。
  • 短地址策略:启用后可减少 URL 长度与路由表规模,但需确保短链与完整模板一致,避免额外分支。
  • 中间件链:默认栈最小化且按需入栈(features.user),路由级细化可精确控制中间件数量与参数,降低不必要处理。
  • 视图赋值与主题扩展:仅在命中后执行,避免未命中路径的额外开销。

故障排查指南

  • 未匹配路由:FrontResolver 会记录警告日志(channel=route),包含 route/lang/is_home 等信息;Router 在未命中时返回 page_wrong。
  • 控制器不存在:若命中条目的 controller 为空或类不存在,记录失败日志并返回 notFound。
  • CSRF 校验失败:CsrfMiddleware 抛出 DomainException,提示页面过期并重定向首页;可通过路由级 withoutMiddleware(['csrf']) 豁免外部回调。
  • 会员认证失败:UserAuthMiddleware 拒绝时跳转登录页或返回 JSON 401(XHR from=js);确认 features.user 与 auth('front') 配置。
  • 短地址问题:启用 ShortUrlPolicy 后,禁止「模块名/…」长格式;若短链未命中,会自动补回模块前缀重试。

结论

DouPHP 前台路由解析器以声明式为核心,通过 LangPrefixParser、PrettyRouteMatcher、FrontResolver 与 Router 的协同工作,实现了高内聚、低耦合的请求分发机制。其优势包括:

  • 纯声明式路由:集中管理于 RouteManifest 的 declared 条目,便于维护与诊断。
  • 高性能匹配:预编译正则、优先级排序与方法感知过滤。
  • 灵活中间件:默认安全栈与路由级细化结合,兼顾安全与可扩展性。
  • 主题集成:在控制器执行前加载主题扩展,支持页面级增强。
  • 表单装配:create/edit 自动装配表单目标与方法,简化模板开发。

附录

  • 常用路由声明示例:
    • 用户模块:group/prefix/sub/name/get/post/resource 组合,定义公开登录与会员中心资源。
    • 产品模块:column 风格展开为列表/分类/详情,控制器为 ProductController。
  • 中间件配置:
    • 默认栈:security_headers、trust_proxy、throttle、[user_auth]、csrf。
    • 路由级细化:without/append/params/skip_all。
  • 调试建议:
    • 查看未匹配日志(channel=route)。
    • 检查 ShortUrlPolicy 配置与短链可逆性。
    • 验证 features.user 与 auth('front') 配置。
    • 使用 route-list 诊断(permissive 模式)查看匹配顺序。
添加日期:2026-10-05