TP6路由到多级控制器必须用“目录名.控制器类名/操作方法”格式,且文件路径、命名空间、路由写法三者严格对应;斜杠分隔写法已弃用,路由注册后须执行php think route:clear清除缓存。

直接结论:TP6 路由到多级控制器,必须用 目录名.控制器类名/操作方法 格式,且控制器文件路径、命名空间、路由写法三者必须严格对应;TP5 可用点号或斜杠,但 TP6 已弃用斜杠写法。
路由地址必须用 目录名.控制器类名/操作方法 写法
ThinkPHP 6 不再支持 index/article/News/index 这类斜杠分隔的多级写法。它只认 article.NewsController/index 这种点号连接的格式,其中 article 是 controller 下的子目录名,NewsController 是类名(不含 .php 后缀)。
- 错误示例:
Route::get('news', 'index/article/NewsController/index')→ 404 或“控制器不存在” - 正确写法:
Route::get('news', 'article.NewsController/index') - 若控制器在
app\index\controller\article\NewsController.php,则命名空间必须是app\index\controller\article,不能漏掉article - TP6 默认不自动补全应用名(如
index),所以index/article.NewsController/index通常多余,除非你明确启用了模块/应用前缀
Route::get 和 Route::rule 的行为差异影响多级路由生效
Route::get 是快捷封装,不支持正则约束、中间件绑定,也不处理变量兼容性问题;而多级控制器常伴随动态参数(如版本号 v1、v2),这时必须用 Route::rule 显式控制解析逻辑。
- 简单场景可用:
Route::get('api/v1/user', 'v1.UserController/index') - 带变量且需兼容特殊字符(如
v1.2、beta-test)时,必须用:Route::rule('api/<version>/user', 'v1.UserController/index', 'GET', ['version' => '[a-zA-Z0-9.-]+'])</version> -
:version是 TP5 风格,TP6 推荐统一用<version></version>,否则可能被忽略或匹配失败 - 修改路由后必须执行
php think route:clear,否则缓存导致新规则不生效
控制器文件位置、命名空间、类名三者对不上就 404
多级控制器不是“建个目录就行”,框架靠路径推导命名空间,再靠命名空间加载类。任意一环断开,就会报“找不到控制器类”。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
立即学习“PHP免费学习笔记(深入)”;
- 文件路径:
app\index\controller\api\v1\UserController.php - 命名空间必须为:
app\index\controller\api\v1(注意不是app\index\controller) - 类名必须与文件名一致:
class UserController,不能写成User或UserCtrl - 路由中写法应为:
api.v1.UserController/index(点号连接全部层级,不含app\或index\前缀) - 如果用的是多应用模式(如
api是独立应用),那控制器应放在app\api\controller\v1\UserController.php,命名空间为app\api\controller\v1,路由写成v1.UserController/index即可(无需api.)
空控制器和多级目录共存时容易混淆入口
当同时启用空控制器(Error.php)和多级目录时,框架会优先按路由匹配,未命中才回落到空控制器逻辑。但如果你把 Error.php 放在 controller 根目录,它只能捕获根级控制器缺失,无法接管 article/xxx 或 api/v1/xxx 这类路径下的缺失请求。
- 想让空控制器覆盖所有多级路径,需把它放到对应子目录下,比如
controller\article\Error.php,并确保命名空间是app\index\controller\article - 更稳妥的做法是统一用路由兜底:
Route::any('<path>', 'Index/error')->append(['path' => '\S+'])</path>,然后在IndexController->error()里做统一处理 - 不要依赖
CONTROLLER_LEVEL配置来“自动降级”——TP6 已移除该配置项,强行设置无效
最易被忽略的一点:TP6 的路由文件默认只加载 app/route.php,你如果把多级路由写在 route/api.php 或 app/api/route.php,不手动 include 或用 Route::import() 引入,那些路由根本不会注册——连报错都不会有,只是静默失效。


















