ThinkPHP6 自定义路由我加了 `route_complete` 中间件,结果全站 404 了半小时才想起优先级这茬

站长杂谈 23 浏览 0 回复 返回上级

昨晚十一点准备给新模块上线,本地测试一切正常,打包上传、清缓存、刷新页面——全站 404。不是某个页面,是全部,连首页都报 "模块不存在"。

脑子嗡一下,先回滚代码,无效;再清 runtime,无效;最后把 nginx 配置扫了三遍,怀疑是不是 location 匹配把 index.php 绕过去了。折腾半小时,突然瞄到路由文件里那行:

`Route::group('api', function () { ... })->middleware(\app\middleware\RouteComplete::class);`

问题就出在这个 `RouteComplete` 上。这中间件是我写的,本意是做路由补全:有些老接口带了空参数,比如 `/user//profile`,想自动纠成 `/user/profile`。逻辑很简单,正则替换完 `$_SERVER['REQUEST_URI']` 再 `return $next($request)`。

但 ThinkPHP6 的路由调度机制里,`App::init()` 之后才会解析路由,而中间件是在 `Http::run()` 阶段逐层包裹的。我那个中间件改了 URI,却没触发路由重新解析,导致框架拿着被改过的 URI 去匹配,原路由规则全失效。更坑的是,这个中间件我挂在了一个全局 `Route::group` 上,等于所有路由都经过它手。

真正让我破防的是排查顺序。我第一反应永远是"代码有没有改错""缓存有没有清干净",完全没往"中间件执行时机"想。后来翻源码,`Middleware` 类的 `pipeline` 实现其实写得很清楚:`$response = $middleware($request, $response, $next)`,但 `request` 对象里的路由信息是只读的,你改了 URI,路由调度器不认。

最后的解法很别扭:把逻辑从中间件挪到 `public/index.php` 里,在 `$http = (new App())->http` 之前做 URI 预处理。虽然丑,但至少不跟框架抢控制权。

这事给我整了个教训:TP6 里路由相关的钩子,优先级和时机比代码逻辑重要一百倍。`route_missed`、`route_complete` 这些命名听着像官方事件,其实全是自定义中间件,挂错了位置就是全局灾难。现在我在路由文件顶部贴了张便签:"加中间件前,先确认它是只读请求还是改写请求。"

你们有没有被中间件执行顺序坑过?我查了下,TP6 文档里关于 `Middleware` 优先级的那节,例子全是 API 鉴权场景,路由改写这种脏活基本没提,算是个隐性坑位了。

评论0
回复 · 0
还没有回复
微信客服 微信客服