ThinkPHP 6 API版本控制应采用路由分层+版本参数提取+服务层隔离:通过带pattern约束的变量路由统一解析v1/v2,控制器中动态加载对应版本服务类,并配合中间件校验版本合法性及兜底默认v1。

ThinkPHP 6 实现 API 接口版本控制(v1/v2),核心是**路由分层 + 版本参数提取 + 服务层隔离**,而不是为每个版本重复写一堆路由和控制器。直接硬编码 /v1/user 和 /v2/user 路由会导致逻辑冗余、维护困难、易漏同步。
用变量路由统一解析版本号
TP6 默认不把 v1、v2 当作特殊标识,需通过带约束的变量路由实现统一入口:
- 在
route/app.php中定义分组路由,将版本号作为可选路径变量:Route::group(['prefix' => 'api'], function () {<br> Route::rule(':version/<module>/<action>', 'api/:module/:action')<br> ->pattern(['version' => 'v[12]']); // 仅匹配 v1 或 v2,防误匹配 v999<br>});</action></module> -
:version不会自动注入控制器方法,需显式获取:$version = $this->request->param('version', 'v1'); - 注意:必须加
->pattern(),否则:version会贪婪匹配任意字符串(如vabc),破坏路由稳定性
控制器中按版本加载对应服务类
避免在控制器里堆砌 if ($version === 'v1') {...} else {...},应把差异下沉到服务层:
- 约定目录结构:
app/service/v1/UserService.phpapp/service/v2/UserService.php - 控制器中动态加载并校验类存在:
$serviceClass = "app\service\{$version}\UserService";<br>if (!class_exists($serviceClass)) {<br> throw new HttpException(400, 'Unsupported API version');<br>}<br>$service = new $serviceClass(); - 两个服务类可实现同一接口(如
UserServiceInterface),确保方法签名一致,仅内部逻辑不同(如字段过滤、校验强度、返回结构)
配合中间件做全局版本拦截与默认兜底
对所有 API 请求统一处理版本兼容性与错误响应:
- 创建中间件
app/middleware/ApiVersionCheck.php,检查:version是否合法,非法时返回 400 并提示支持的版本列表 - 在路由分组中注册该中间件:
Route::group(['prefix' => 'api', 'middleware' => 'ApiVersionCheck'], function () { ... }); - 若未传版本(如访问
/api/user/list),param('version', 'v1')自动回退到默认版本,保证向后兼容
避免常见陷阱
这些做法看似省事,实则埋下长期维护隐患:
- ❌ 在 route.php 中手动写两套路由:
Route::get('api/v1/user', [...])和Route::get('api/v2/user', [...])—— 新增 v3 时要改多处,极易遗漏 - ❌ 把版本判断写进每个控制器方法 —— 控制器职责膨胀,测试成本高,无法复用基础 service
- ❌ 使用 Accept 头做版本控制(如
Accept: application/vnd.myapi.v2+json)—— TP6 对内容协商支持弱,缓存、CDN、调试工具(如 Swagger)均不友好,容易触发 406 错误











