ThinkPHP 多应用模式下 `route.php` 我配了八遍,前端请求还是 404:原来 `app_map` 把应用名"吃"掉了
上周给客户改个老项目,从单应用切到多应用模式。本地环境跑得好好的,一部署到测试机,所有接口集体 404。我盯着 `route/app.php` 看了二十分钟,路由规则写得明明白白,咋就不生效呢?
先说我怎么排查的,给同样踩坑的兄弟参考:
第一步:确认路由文件到底加载没
我在 `route.php` 第一行直接 `die('route loaded')`,刷新页面,没反应。好,文件压根没执行。这时候别急着改路由,先去看应用入口对不对。
多应用模式下,我配了 `app_map` 做应用映射:
'app_map' => [
'api' => 'api', // 域名/api/xxx 指向 api 应用
'admin' => 'admin',
],
问题就出在这。我前端请求的 URL 是 `/api/v1/user/list`,但 `app_map` 匹配到 `api` 后,会把 `api` 这一段从 URL 里抹掉,实际进入路由解析的是 `/v1/user/list`。而我 `route/app.php` 里写的是:
Route::get('api/v1/user/list', 'v1.User/index');
前面多了个 `api/`,当然匹配不上。本地为啥能跑?因为我本地没配 `app_map`,用的默认应用绑定,URL 完整进路由,反而能对上。
第二步:钩子也不触发,连带坑
路由不通之后,我顺手测试了个全局中间件,发现 `app_init` 钩子里的日志也没写进去。当时心里一凉,以为整个应用生命周期都崩了。后来才反应过来:请求在路由匹配阶段就 404 了,根本没走到中间件和钩子执行那一步。
这俩问题其实是一个根子,别被表象带偏去查钩子注册。
第三步:多应用路由文件的"隐身"规则
ThinkPHP 多应用模式下,路由文件加载顺序是:
1. `route/route.php`(全局,所有应用共享)
2. `route/应用名/route.php`(应用专属)
3. `应用目录/route/route.php`(应用内部)
我之前把路由写在 `route/api/route.php`,但 `app_map` 映射后应用标识变了,文件路径对不上。最后统一收拢到 `route/route.php`,用路由分组做应用隔离,反而清爽:
Route::group('api', function () {
Route::get('v1/user/list', 'api/v1.User/index');
})->app('api');
一点血泪总结
多应用模式切之前,建议先把 `app_map`、`domain_bind`、`deny_app_list` 这几个配置打印出来,对着 URL 走一遍解析流程。我这次就是太相信"本地能跑",没意识到本地和测试机的应用映射配置差了一条。
还有,路由不生效时,`php think route:list` 这个命令能救命,但它只列当前默认应用的路由。多应用下要加 `--app api` 指定应用名,我愣是查了半小时文档才看见这参数。
现在项目上线了,我把 `app_map` 的映射逻辑和路由实际匹配路径写进了项目 README,下次谁再接这个锅,至少不用从头踩一遍。

