php think make:middleware 命令默认将中间件生成在 app/middleware/ 目录下,文件名与类名严格一致,命名空间固定为 app\middleware;多应用模式需手动移至子目录(如 app/admin/middleware/)并更新命名空间。

php think make:middleware 命令生成的文件放哪
命令默认把中间件类生成在 app/middleware/ 目录下,文件名和类名严格一致,命名空间固定为 app\middleware。比如执行 php think make:middleware AuthCheck,就会创建 app/middleware/AuthCheck.php,且开头必须是 namespace app\middleware;。
多应用模式(如 app/admin)下,命令不会自动适配子应用路径——它仍会生成到 app/middleware/,你得手动挪到 app/admin/middleware/AuthCheck.php,并同步改命名空间为 app\admin\middleware。别图省事往 app/middleware/auth/ 这类子目录里放,框架根本不扫描子目录。
常见错误:
- 手动生成文件却忘了写命名空间,或写成
app\http\middleware(那是 TP5 的旧路径) - 文件开头有 BOM、空格、
echo或注释外的输出,导致app/middleware.php加载时直接报错或静默失败
中间件 handle() 方法签名和返回值不能错
handle() 是唯一入口,签名必须是 handle($request, \Closure $next),且必须返回 think\Response 实例。漏掉 return、返回 null、string、array 或用 die()/exit(),请求就中断无声,连 500 都不报。
立即学习“PHP免费学习笔记(深入)”;
正确放行写法:return $next($request); —— 这是进入控制器的唯一通道。
拦截写法示例:
return json(['code' => 401, 'msg' => '未登录']);return redirect('/login');
传参支持:路由注册时加参数,如 ->middleware(\app\middleware\AuthCheck::class, 'admin'),则 handle() 可多接一个参数:handle($request, \Closure $next, $role)。
中间件注册位置决定它是否生效
注册错了,中间件根本不会被调用。三种主流注册方式对应不同加载时机:
-
全局注册:写在
app/middleware.php,该文件必须存在、必须返回数组、数组元素必须是完整类名字符串(如app\middleware\AuthCheck::class或'app\middleware\AuthCheck')。删了它、写了echo、返回null,整个中间件系统就瘫痪。 -
路由注册:必须显式链式调用,如
Route::get('user', 'User/index')->middleware(\app\middleware\AuthCheck::class)。只写路由不加->middleware(),等于没注册。 -
控制器注册:在控制器构造函数中写
$this->middleware(\app\middleware\AuthCheck::class);,仅对当前控制器方法生效;若用protected $middleware = ['app\middleware\AuthCheck'];,需确认控制器继承的是think\Controller(TP6 推荐用think\facade\Controller,但该写法在部分版本中已弃用)。
前置 vs 后置中间件的关键区别在哪
所谓“前置”或“后置”,只取决于你的逻辑写在 $next($request) 前还是后,不是框架配置出来的。
前置写法(推荐用于权限拦截):
public function handle($request, \Closure $next)
{
if (!$request->session('user_id')) {
return redirect('/login');
}
return $next($request); // 放行才进控制器
}
后置写法(适合日志、响应处理):
public function handle($request, \Closure $next)
{
$response = $next($request); // 控制器已执行完毕
return $response->header('X-Processed', 'yes');
}
关键差异:
- 前置中间件中,
$request->controller()和$request->action()通常为空(路由尚未解析完成);后置中间件中能正常获取。 - 后置中间件拦截无效——控制器代码已经执行完了,比如数据库写入、文件操作都发生了,再重定向也晚了。
真正容易被忽略的点:中间件执行是洋葱模型,多个中间件嵌套调用,但每个 handle() 方法本身仍是线性执行。别指望在前置中间件里“提前拿到控制器名”去做判断,那得靠路由变量或请求参数兜底。



















