中间件问题主要出在注册位置、handle()返回值和参数传递方式上:全局中间件$middleware不适用于api鉴权,应使用$middlewaregroups['api']或$routemiddleware;必须return $next($request)或显式响应;传参需通过字符串解析而非构造函数。

中间件不生效、请求卡死、参数传不进去——这些问题基本都出在注册位置、handle() 返回值、或参数传递方式上,而不是逻辑写错了。
中间件注册位置错了,请求根本进不来
你写了 CheckToken 类,也运行了 php artisan make:middleware CheckToken,但请求压根没走到它的 handle() 里。最可能的原因是:它被塞进了错误的注册数组。
-
$middleware(全局):所有 HTTP 请求都走,包括/health、静态资源、甚至队列触发的 HTTP 请求;不适合做 API 鉴权 -
$middlewareGroups['api'](分组):只对显式标记为->middleware('api')的路由生效;适合放throttle、auth:sanctum这类 API 专用中间件 -
$routeMiddleware(路由级):必须先在这里命名,比如'check.token' => \App\Http\Middleware\CheckToken::class,再在路由中调用->middleware('check.token');适合按需启用的自定义中间件
查错最快方式:在 handle() 开头加一句 Log::debug('check.token hit');,然后看日志有没有输出。没输出,八成是注册位置或路由绑定漏了。
handle() 里漏了 return $next($request),请求就挂了
这是最隐蔽也最常踩的坑。浏览器转圈、cURL 超时、返回空响应——不是报错,就是没响应,十有八九是这里断链了。
- 中间件不是“过滤器”,而是“拦截器 + 转发器”;
$next($request)是把控制权交给下一个中间件或控制器的唯一入口 - 即使你要提前终止,也必须 显式 return 一个响应,比如
return response()->json(['error' => 'Forbidden'], 403); - 写成
$next($request);(没 return)或redirect()->to('/login');(少 return)都会导致静默失败 - 别用
die或exit,会绕过 Laravel 的异常处理和响应格式化
想传参给中间件?别碰构造函数,用字符串解析
Laravel 不支持 ->middleware(CheckRole::class . ':admin') 这种写法自动注入参数。它实际是靠字符串拆分+手动解析实现的。
- 注册到
$routeMiddleware后,在路由里写->middleware('role:admin,editor') - 中间件
handle()方法签名要改成:public function handle($request, Closure $next, $roles) -
$roles就是字符串'admin,editor',你需要自己explode(',', $roles)处理 - 构造函数里不能依赖
$request或Auth::user(),因为实例化发生在请求到达前,此时上下文未初始化
IP 判断、请求方法过滤,别手撕 $_SERVER
用原生 PHP 变量判断 $_SERVER['REQUEST_METHOD'] 或 $_SERVER['REMOTE_ADDR'],在反向代理(Nginx / Cloudflare)下大概率失效。
- 统一用
$request->method()判断动词,abort_if($request->method() !== 'POST', 405)更稳 - 用
$request->ip()获取客户端 IP,但必须配好TRUSTED_PROXIES环境变量,否则拿到的是 Nginx 内网地址 - 检查参数是否存在,优先用
$request->has('token'),比!empty($request->token)更准(能区分null、''、0) - 跳过日志记录等非核心逻辑时,用
$request->is('health', 'storage/*'),比字符串匹配 URL 更可靠
真正难的不是写逻辑,而是搞清中间件在哪一层执行、上下文是否就绪、以及返回值是否被框架正确接收——这些地方一错,问题就藏得深、表现得怪。











