最稳方案是用 route::group() 统一声明版本前缀,避免中间件分发或路由硬编码;tp6.1+ 支持前缀与中间件绑定,语义清晰、可复用、兼容路由缓存。

直接用 Route::group() 套前缀是最稳的方案,别在中间件里做版本分发,也别把 v1、v2 写死在每条路由里——维护成本高、容易漏、还绕过路由缓存。
用 Route::group() 统一声明版本前缀
TP6.1+ 支持在分组中绑定前缀和中间件,语义清晰、可复用、不污染全局。关键点是:前缀必须显式写进分组定义,不能靠字符串匹配或中间件解析路径。
Route::group('api/v1', function () { Route::get('user', 'v1.User/get'); })->middleware(JwtAuth::class);Route::group('api/v2', function () { Route::get('user', 'v2.User/get'); })->middleware(JwtAuth::class);- 不要写
Route::get('api/v1/user', ...)这类散装路由,否则新增接口时极易漏加v1或写错命名空间 - 分组内闭包函数体里注册的路由,会自动继承前缀,且能被
php think route:list正确识别
支持变量版本号但限定范围
如果想让 /api/v1/user 和 /api/v2/user 共用同一组路由规则(比如只改控制器命名空间),可以用变量路由 + pattern 约束,避免匹配到非法版本如 v999。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
Route::group(['prefix' => 'api'], function () { Route::rule(':version/<module>/<action>', 'api/:version.:module/:action')->pattern(['version' => 'v[12]']); });</action></module>-
:version不会自动注入控制器方法,得手动调request()->param('version')拿值 - 必须加
->pattern(),否则:version会匹配任意非斜杠字符,导致/api/xxx/user也被误认 - 这种写法适合灰度发布或快速验证,但正式项目建议用分组——更可控、调试友好、IDE 能跳转
控制器层做协议适配,不是路由层
版本差异本质是输入校验、字段映射、业务逻辑变化,这些不该塞进路由或中间件。路由只管分发,转换逻辑下沉到服务层。
- v2 接口要兼容 v1 的参数但返回新字段?在
v2.User控制器里调v1\UserService方法,再包装响应:return json(['data' => $v1Data, 'extra_v2_field' => 'ok']); - 字段名变更(如
user_name→name)用Arr::only($data, ['id', 'user_name'])映射,比一堆if ($version === 'v2') { ... }更易维护 - 禁止在中间件里改
input()数组——会破坏原始请求快照,审计、重放、日志全不准 - 若用工厂加载服务类,务必加
class_exists($serviceClass)判断,否则v3请求直接抛Class not found,应返回 400
中间件必须按分组绑定,不能靠路径字符串匹配
TP 不解析 URL 路径字符串来决定中间件是否生效,只认路由定义时的规则或分组名。全局注册 ['api/v1' => [...]] 在 middleware.php 里完全无效。
- 正确做法:分组链式绑定,如
Route::group('api/v2')->middleware([JwtAuth::class, RateLimit::class]) - v1 不需要鉴权、v2 需要?就分开写两个分组,别试图在闭包里
if (request()->pathinfo() === 'api/v2') {...}动态挂载——这会让中间件执行顺序不可控,且无法被路由缓存优化 - 注意:Swoole / RoadRunner 环境下禁用闭包路由,
Route::group('api/v1', function () { ... })中的闭包无法序列化,会报Serialization of 'Closure' is not allowed
最常被忽略的是命名空间与目录结构的一致性:v1.User/get 对应的类必须是 app\controller\v1\User,且文件路径为 app/controller/v1/User.php;少一个字母或大小写不一致,就会 404 或 Class not found。路由写得再漂亮,控制器找不到,一切白搭。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










