中间件应在请求或响应需统一拦截、检查、修改且逻辑不绑定控制器时使用;适用于登录校验、token验证、限流、日志记录、cors头注入等前置/后置通用处理,而非替代业务逻辑或细粒度权限判断。

中间件该在「请求或响应需要被统一拦截、检查、修改,且逻辑不绑定到具体控制器方法内部」时用。它不是万能钩子,也不是替代验证或业务逻辑的捷径。
需要提前拦截请求的场景
比如用户未登录就访问后台页、API 请求没带有效 token、请求体过大、IP 被限流——这些判断必须在控制器执行前完成,否则浪费资源还可能暴露内部状态。
-
auth中间件在路由匹配后、控制器调用前就终止未登录请求,避免进入DashboardController@index再做判断 -
throttle:60,1必须在早期执行,否则攻击者可绕过限制反复触发数据库查询 -
ValidatePostSize放在TrimStrings之前,否则超大请求可能在清理阶段就崩溃
需要统一处理响应的场景
日志记录、缓存头设置、CORS 响应头注入、会话保存——这些操作依赖完整响应内容,不能在控制器里零散写。
-
StartSession的terminate()方法在响应已生成但尚未发送时写入 session cookie,放太早没数据,放太晚发不出 -
HandleCors必须在响应即将返回前注入Access-Control-Allow-Origin,否则浏览器直接拒绝跨域请求 - 自定义日志中间件若只在
handle()里记请求,漏掉响应体大小和耗时,得靠terminate()补全
不适合用中间件的地方
当逻辑强依赖路由参数、模型实例或控制器上下文时,中间件反而增加耦合和调试难度。
- 控制器构造函数里用
$this->middleware('admin')->only(['destroy'])是可行的,但若要检查$request->route('user')是否属于当前用户,别在构造函数里做——路由参数此时还没解析出来 - 权限校验如「用户能否编辑某篇文章」更适合放在 Policy 或 Controller 方法开头,而不是塞进中间件;中间件适合做「用户是否已登录」「是否是管理员」这类粗粒度判断
- 表单提交的数据清洗(如 trim、转义)应该由 Form Request 或控制器内手动处理,
TrimStrings这类全局中间件只做基础标准化,不该承担业务规则
参数化中间件容易踩的坑
用 role:admin 这类带参中间件时,handle() 方法签名必须严格匹配参数个数和顺序,且所有参数都是字符串类型。
- 路由写
middleware('role:admin,posts'),中间件里就得声明handle($request, $next, $role, $resource),少一个参数就报Too few arguments -
throttle:60,1中的60和1是字符串,不能直接当整数用,得(int) $maxAttempts - 参数里含逗号或冒号(比如角色名是
dev,ops)会导致解析错乱,这种场景该改用请求头或 query 参数传,别硬塞中间件参数
真正难的不是写中间件,而是决定哪段逻辑该放进中间件、哪段该留在控制器或 service 层。边界模糊时,优先选更靠近业务的位置——中间件越薄,越不容易成为黑盒陷阱。











