必须用 php artisan make:middleware 创建中间件类,否则即使文件存在且命名规范,laravel 也不会识别;生成的类需保留 handle($request, closure $next) 签名,命名空间与 psr-4 严格匹配。

中间件类怎么创建才不会被 Laravel 忽略
Laravel 不会自动加载你随手新建的 PHP 文件,中间件必须通过 php artisan make:middleware 创建,否则即使类存在、命名规范,app/Http/Middleware/ 下也不会被框架识别。
常见错误现象:写完类、在 Kernel.php 里注册了,但请求完全不进中间件——八成是没用 Artisan 命令生成,导致缺少 handle 方法签名或命名空间错位。
- 必须用
php artisan make:middleware CheckAdminRole,不要手动新建文件 - 生成的类默认有
public function handle($request, Closure $next),别删掉Closure $next参数,否则$next($request)会报错 - 类名要符合 PSR-4,比如
CheckAdminRole对应文件名CheckAdminRole.php,不能写成checkadminrole.php或check_admin_role.php
权限判断逻辑写在哪?别在 handle 里查数据库
中间件的 handle 方法执行频次极高,每次请求都走一遍。如果在里面调 Auth::user()->roles()->get() 这类 Eloquent 查询,接口延迟立刻翻倍,还容易触发 N+1。
正确做法是复用已加载的数据,比如 Laravel 自带的 auth guard 已经把用户实例和基础权限缓存在内存里,优先用 $request->user() + 预加载字段或缓存好的权限标识。
- 查角色推荐用
$request->user()?->hasRole('admin')(需配合 spatie/laravel-permission 等扩展),而不是重新查库 - 自定义权限如 “能编辑当前文章”,把
$post->user_id和$request->user()->id对比即可,别再查一次Post::find() - 需要复杂判断时,提前在
AppServiceProvider的boot里绑定一个轻量服务,中间件里app(MyPermissionChecker::class)->can(...)
路由组里注册中间件,为什么有些请求还是没拦截
最常漏掉的是中间件参数传递方式。比如想限制「只有拥有 post.edit 权限的人才能访问编辑页」,写成 middleware(['can:post.edit']) 是对的;但若写成 middleware('can:post.edit')(单引号没包数组),Laravel 会当字符串处理,直接忽略参数,变成无条件放行。
另外,can 中间件依赖 Laravel 的 Gate 机制,如果你没在 AuthServiceProvider@boot 里用 Gate::define('post.edit', ...) 定义规则,它永远返回 false,但不会报错,只会静默拒绝。
- 带参数的中间件必须用数组语法:
middleware(['can:post.update', 'throttle:30,1']) -
can中间件底层调Gate::allows(),没定义对应规则就一律 deny,检查app/Providers/AuthServiceProvider.php的boot方法 - 测试时别只测 GET,POST/PUT 请求可能因 CSRF 或表单验证提前终止,根本到不了中间件层
中间件里 redirect() 后为什么页面空白或跳错
直接写 return redirect('/login') 没问题,但很多人会写 redirect()->route('login') 却忘了在 routes/web.php 里真有叫 login 的命名路由——结果抛出 Route [login] not defined 错误,而这个异常在中间件里常被静默吞掉,只显示空白页。
更隐蔽的问题是重定向目标带参数时没做空值判断。比如 redirect()->route('post.show', ['post' => $post->id]),但 $post 是 null,就会触发 Null passed to route parameter。
- 所有
route()调用前,先确认命名路由存在:php artisan route:list | grep login - 涉及模型 ID 的重定向,加一层存在性检查:
if (!$post) { return redirect('/'); } - 想返回上一页,用
redirect()->back(),但注意它依赖浏览器Referer头,API 请求或某些代理环境下可能为空,建议 fallback 到固定路径









