中间件是请求与响应之间的“守门人”,核心行为为拦截、转发或终止;handle()方法接收$request和$next,调用$next($request)放行,不调用则终止流程,支持前后处理请求与响应。

中间件就是请求和响应之间的“守门人”——它不处理业务逻辑,但决定请求能不能进、以什么状态进、带着什么信息进,以及响应出去前要不要加工或拦截。
中间件的核心行为:拦截 + 转发 + 终止
每个中间件的 handle() 方法都接收两个强制参数:$request 和 $next。关键就在这两件事上:
- 调用
$next($request)表示“放行”,把请求交给下一个中间件或最终控制器 - 不调用
$next()(比如直接返回response()->json(...)或abort(403))就等于“终止流程”,后续中间件和控制器都不会执行 - 可以在调用
$next()前修改$request(如添加属性、解析 token),也可以在它返回后处理$response(如加 header、压缩内容)
为什么不能直接写在控制器里?
因为重复逻辑会污染业务代码。比如权限检查,如果每个控制器方法开头都写一遍 if (! $user->can('delete')) abort(403),既难维护又易漏。中间件把这类横切关注点(cross-cutting concern)抽出来统一管理:
-
auth中间件负责登录态校验,所有加了->middleware('auth')的路由自动获得保护 -
throttle:60,1控制每分钟最多 60 次请求,不用改任何控制器代码 - 自定义
check.age可复用于注册页、问卷页等多个路由,参数还能动态传入(如middleware('check.age:21'))
中间件参数怎么传?常见陷阱有哪些
路由中用冒号分隔参数:middleware('role:admin,editor'),对应 handle($request, $next, $roles) —— 注意:$roles 是字符串 "admin,editor",不是数组。要拆开得手动 explode(',', $roles)。
- 想传多个独立参数?用逗号分隔即可:
middleware('cache:60,public')→handle($request, $next, $ttl, $type) - 参数里含冒号或逗号?Laravel 不支持转义,此时应改用配置项或请求头传递,避免解析歧义
- 全局中间件(
$middleware数组)无法接收路由参数,只适用于无条件执行的逻辑(如日志、信任代理设置)
terminate() 方法容易被忽略,但它真有用
有些操作不适合在请求阶段做,比如记录耗时、写访问日志、清理临时 session 数据。这时可以定义 terminate($request, $response) 方法,它会在响应已发送给客户端后异步触发:
- 必须在
Kernel.php的$middleware或$routeMiddleware中注册该类,且类需实现TerminableMiddleware接口(实际只需有terminate方法) - 注意:
terminate()里不能再修改响应体或 header,HTTP 状态码也已发出 - 典型用途:统计请求耗时、异步上报错误、释放大对象引用防止内存泄漏











