全局 app/middleware.php 在多应用模式下失效,因框架仅加载对应应用目录下的 middleware.php;每个应用需单独配置如 app/admin/middleware.php,并确保文件存在、命名空间匹配、handle 方法正确返回响应。

多应用模式下,全局 app/middleware.php 的中间件配置完全不生效——必须为每个应用单独配 app/{app_name}/middleware.php。
为什么全局 middleware.php 在多应用下失效
ThinkPHP 6+ 启用 app/multi_app = true 后,框架会按应用目录隔离加载逻辑。请求进入时,框架只读取对应应用目录下的 middleware.php,根本不会扫描根目录的 app/middleware.php。
常见错误现象:app/middleware.php 里写了 \think\middleware\LoadLangPack::class,但语言切换始终不触发,lang() 返回键名而非翻译值。
- 多应用项目结构示例:
app/admin/、app/api/、app/web/ - 每个子应用必须有自己独立的
app/admin/middleware.php文件 - 文件必须存在且返回数组,不能是空文件、注释文件或含
echo/var_dump - 命名空间需匹配应用路径:比如
app/admin/middleware.php中的中间件类,命名空间应为app\admin\middleware
如何正确注册 LoadLangPack 中间件(多应用场景)
以 app/admin 应用为例,语言包路径为 app/admin/lang/en-us.php,中间件必须在该应用下显式注册并确保执行顺序。
立即学习“PHP免费学习笔记(深入)”;
- 创建
app/admin/middleware.php,内容为:
return [
\think\middleware\LoadLangPack::class,
\think\middleware\SessionInit::class,
];
- 确认
app/admin/config/lang.php中关键项已设为布尔值:'lang_switch_on' => true、'default_lang' => 'zh-cn'、'lang_list' => ['zh-cn', 'en-us'] - 别把
LoadLangPack放在Cors或其他可能提前终止响应的中间件之后——它必须在 Session 初始化后、路由解析前运行 - 类名必须完整且大小写精确:
\think\middleware\LoadLangPack,写成LangPack或loadlangpack都会静默失败
自定义中间件在多应用中怎么放、怎么引用
手写中间件类时,路径和命名空间错一个字符就加载失败,框架不报错也不提示。
- 生成方式最稳妥:在
app/admin目录下运行php think make:middleware AdminAuth,它会自动建在app/admin/middleware/AdminAuth.php,命名空间为app\admin\middleware - 手动创建时,文件路径必须是
app/admin/middleware/AdminAuth.php,不能是app/admin/middleware/auth/AdminAuth.php(子目录不扫描) - 在
app/admin/middleware.php中引用:直接写\app\admin\middleware\AdminAuth::class - 若同时在
app/api用同一逻辑,必须复制一份到app/api/middleware/AdminAuth.php,并改命名空间为app\api\middleware
容易被忽略的细节:中间件 handle() 必须 return Response
哪怕只是做语言设置,handle() 方法也必须最终调用 $next($request) 并返回其结果,否则请求链中断,控制器根本不会执行。
- 错误写法:
Lang::setLang('en-us'); // 没有 return $next($request)→ 页面空白或 500 - 正确写法:
Lang::setLang('en-us'); return $next($request); - 如果中间件里做了重定向或抛异常,也要显式 return:
return redirect('/login');或throw new HttpException(403); - 多应用下,
SessionInit和LoadLangPack的顺序不能颠倒——没初始化 Session 就读不到cookie('think_lang')
多应用中间件真正生效的临界点,往往卡在「文件是否存在」「命名空间是否匹配」「是否被框架扫描到」这三处;一旦漏掉其中任一环,就不会进 handle(),更不会报错,只能靠逐层确认路径和返回值来定位。



















