简介
本文件为 DouPHP 的 API 版本控制设计文档,聚焦以下目标:
- 明确 API 版本管理策略:URL 路径版本化、请求头版本化、查询参数版本化。
- 制定向后兼容性保证:废弃接口处理、数据格式兼容、错误码演进。
- 提供版本迁移指南:升级步骤、数据迁移脚本、兼容性测试。
- 给出版本路由配置方法与中间件实现方案。
- 建立版本弃用通知与用户迁移提示机制。
当前代码库已具备稳定的 API 入口、声明式路由、中间件栈与统一响应封装,可作为版本控制的承载基础。
项目结构
DouPHP 的 API 子系统采用“入口 → 路由解析 → 中间件管道 → 控制器”的分层结构:
- 入口负责初始化、异常兜底与统一 JSON 响应。
- 路由解析将 URL 映射到控制器与方法,并组装中间件栈。
- 中间件承担安全头、代理信任、限流、鉴权等横切关注点。
- 控制器实现业务逻辑,返回 ApiResponse。
graph TB
A["API 入口<br/>api/index.php"] --> B["路由调度器<br/>Router::dispatch()"]
B --> C["API 解析器<br/>ApiResolver::resolve()"]
C --> D["中间件栈<br/>SecurityHeaders / TrustProxy / Throttle / UserAuth"]
D --> E["控制器方法<br/>业务逻辑"]
E --> F["统一响应<br/>ApiResponse"]
图示来源
- api/index.php:27-55
- api/foundation/routing/Router.php:43-68
- api/foundation/routing/ApiResolver.php:60-90
核心组件
- API 入口:设置路由委托、读取 route 查询参数、启动 Init、分发路由、统一异常处理与 JSON 500 渲染。
- 路由调度器:根据 ApiResolver 产出的分发计划执行中间件管道,未匹配或方法不被接受时返回标准 JSON 错误。
- API 解析器:基于声明式路由匹配模块/动作/子控制器,注入路由参数,组合默认与路由级中间件。
- 中间件:
- 安全头:统一响应头。
- 代理信任:正确识别客户端 IP。
- 限流:按路由键限制高频写操作与敏感接口。
- 鉴权:从 Authorization 头提取 token,按 auth_modes 策略放行或拒绝。
- 控制器基类:提供会员中心导航构建等 API 端通用能力。
架构总览
下图展示一次 API 请求从入口到控制器再到响应的完整流程,以及中间件在其中的作用位置。
sequenceDiagram
participant Client as "客户端"
participant Entry as "API 入口"
participant Router as "路由调度器"
participant Resolver as "API 解析器"
participant MW as "中间件栈"
participant Ctrl as "控制器"
participant Resp as "统一响应"
Client->>Entry : HTTP 请求携带版本标识
Entry->>Router : dispatch()
Router->>Resolver : resolve(request, container)
Resolver-->>Router : DispatchPlan含中间件列表
Router->>MW : 依次执行中间件
MW-->>Ctrl : 通过鉴权/限流后进入控制器
Ctrl-->>Resp : 返回 ApiResponse
Resp-->>Client : JSON 响应可包含版本信息
图示来源
- api/index.php:27-55
- api/foundation/routing/Router.php:43-68
- api/foundation/routing/ApiResolver.php:60-90
详细组件分析
版本管理策略
建议同时支持三种版本标识方式,便于不同客户端灵活选择:
- URL 路径版本化:在路由前缀增加版本号段,如 v1、v2。可通过新增路由组或独立路由文件组织不同版本的控制器。
- 请求头版本化:在 Authorization 之外引入 X-API-Version 头,用于在不改变 URL 的情况下切换版本。可在鉴权或自定义中间件中解析该头,并写入 Request 上下文供控制器使用。
- 查询参数版本化:通过 ?version=v1 等参数传递版本,适合调试与快速验证。同样由中间件解析并注入上下文。
实施要点:
- 在 ApiResolver 或自定义中间件中解析上述任一版本源,统一输出 version 到 Request 上下文。
- 控制器依据 version 分支调用不同服务实现或进行字段映射,确保行为一致。
- 对旧版本保留最小兼容层,逐步下线。
向后兼容性保证
- 废弃接口处理:
- 对即将下线接口返回明确的弃用提示与迁移指引,HTTP 状态码保持语义正确(如 410 Gone 或 200 但带 deprecation 字段)。
- 在鉴权/限流中间件之前或之后插入“弃用检测”中间件,按路由键判断是否命中废弃接口。
- 数据格式兼容:
- 控制器内维护多版本序列化器,按 version 输出兼容字段;新增字段仅追加,不删除旧字段。
- 对必填字段变更提供默认值或降级策略。
- 错误码演进:
- 统一使用 ApiResponse 的错误码体系,新增错误码需保持与旧客户端可识别的兼容映射。
- 在响应体中附加 error_code、message、details,便于前端适配。
版本迁移指南
- 升级步骤:
- 新增版本路由组与控制器实现(例如 v2),保持与 v1 一致的 URL 结构与语义。
- 在中间件层解析版本标识并注入上下文。
- 控制器内部按版本分支调用新实现或做字段映射。
- 发布灰度流量,观察错误率与性能指标。
- 客户端逐步切换至新版本,关闭旧版本开关。
- 数据迁移脚本:
- 针对破坏性变更准备迁移脚本,确保新旧版本并存期间数据可读。
- 脚本应幂等、可回滚,并在低峰期执行。
- 兼容性测试:
- 编写端到端用例覆盖新旧版本关键路径。
- 对限流、鉴权、错误码、字段兼容性进行回归测试。
版本路由配置方法
- 基于现有声明式路由扩展:
- 在 api/route 下按版本拆分路由文件(如 v1.php、v2.php),或使用 Route::group 包裹版本前缀。
- 每个版本的路由指向对应版本的控制器命名空间或类名。
- 示例思路:
- v1:/api/?route=v1/user/login
- v2:/api/?route=v2/user/login
- 或通过中间件解析 X-API-Version 与 ?version,动态选择控制器实现。
中间件实现要点
- 鉴权中间件:
- 从 Authorization 头提取 token,解析登录态并注入上下文。
- 结合 middleware.php 中的 auth_modes 配置,按模块/动作粒度决定 public/optional/required。
- 限流中间件:
- 对敏感写接口与公共匿名写接口按 IP 限流,超限返回 429 并附带 Retry-After。
- 弃用检测中间件(建议新增):
- 读取版本上下文,若命中废弃路由则返回弃用提示与迁移指引。
- 版本解析中间件(建议新增):
- 解析 X-API-Version 与 ?version,统一写入 Request 上下文,供后续中间件与控制器使用。
弃用通知与用户迁移提示机制
- 响应头提示:
- 在弃用接口响应中添加 Deprecation、Sunset、Link 等头部,告知客户端废弃时间与替代接口。
- 响应体提示:
- 在 ApiResponse 中附加 deprecation 字段,包含 message、url、sunset_date 等。
- 客户端引导:
- 前端根据 deprecation 字段显示迁移提示,自动切换到新版本路由或参数。
依赖关系分析
- 入口依赖路由调度器与异常处理器。
- 路由调度器依赖解析器与 Dispatcher。
- 解析器依赖声明式路由匹配与中间件注册表。
- 中间件依赖配置与统一响应。
- 控制器依赖服务与门面,返回统一响应。
graph LR
Entry["API 入口"] --> Router["路由调度器"]
Router --> Resolver["API 解析器"]
Resolver --> MWReg["中间件注册表"]
MWReg --> MW1["鉴权中间件"]
MWReg --> MW2["限流中间件"]
MWReg --> MW3["安全头中间件"]
MW1 --> Ctrl["控制器"]
MW2 --> Ctrl
MW3 --> Ctrl
Ctrl --> Resp["统一响应"]
图示来源
- api/index.php:27-55
- api/foundation/routing/Router.php:43-68
- api/foundation/routing/ApiResolver.php:60-90
- api/middleware/UserAuthMiddleware.php:34-93
- api/middleware/ThrottleMiddleware.php:31-90
性能考虑
- 路由匹配与中间件链应尽量轻量,避免在解析阶段进行昂贵 I/O。
- 限流中间件使用高效存储(如内存缓存)记录计数,减少数据库压力。
- 版本解析应在早期完成,避免重复计算。
- 对弃用接口可启用快速失败路径,减少不必要的业务逻辑执行。
故障排查指南
- 未找到路由(404):检查声明式路由是否正确注册,module/action/sub 是否匹配。
- 方法不被允许(405):确认 HTTP 方法与路由声明一致。
- 鉴权失败(401/403):检查 Authorization 头与 auth_modes 配置,确认用户与工作端权限。
- 限流触发(429):查看请求频率与窗口配置,必要时调整配额或优化客户端重试策略。
- 未捕获异常(500):入口会统一记录日志并返回 JSON 500,检查堆栈与站点调试开关。
结论
通过在入口、路由、中间件与控制器各层引入版本标识解析与兼容层,DouPHP 可实现稳健的 API 版本管理。配合弃用通知、错误码演进与迁移指南,能够在不影响现有客户端的前提下平滑升级。建议在每次重大变更时同步更新路由、中间件与测试用例,确保向后兼容性与可观测性。
附录
版本化请求流程图(概念)
flowchart TD
Start(["接收请求"]) --> ParseVer["解析版本标识<br/>URL/X-API-Version/?version"]
ParseVer --> CheckDep{"是否命中废弃接口?"}
CheckDep --> |是| ReturnDep["返回弃用提示与迁移指引"]
CheckDep --> |否| MWChain["执行中间件链"]
MWChain --> Controller["调用控制器"]
Controller --> Serialize["按版本序列化响应"]
Serialize --> End(["返回响应"])
[此图为概念流程,不直接映射具体源码,故无图示来源]
版本路由与中间件装配时序
sequenceDiagram
participant R as "路由调度器"
participant Res as "API 解析器"
participant MR as "中间件注册表"
participant Auth as "鉴权中间件"
participant Thr as "限流中间件"
participant Ctrl as "控制器"
R->>Res : resolve(request, container)
Res-->>R : DispatchPlan(中间件别名列表)
R->>MR : compose(默认+路由级)
MR-->>R : 中间件实例链
R->>Auth : 执行鉴权
Auth-->>Thr : 通过
Thr-->>Ctrl : 通过限流
Ctrl-->>R : ApiResponse
图示来源
- api/foundation/routing/Router.php:43-68
- api/foundation/routing/ApiResolver.php:60-90
- api/middleware/UserAuthMiddleware.php:34-93
- api/middleware/ThrottleMiddleware.php:31-90