直接在中间件 handle() 方法中返回 response 实例即可中断请求流程,但必须确保所有分支都显式返回响应或调用 $next->handle($request),否则隐式返回 null 会导致白屏或 500 错误。

直接在中间件 handle() 方法里返回响应即可中断请求流程,但必须确保返回的是 Response 实例,且不能漏掉任何执行分支的 return。
中间件拦截请求的核心写法
ThinkPHP 中间件不是“开关”,而是 PSR-15 兼容的处理器链——只要不调用 $next->handle($request),或提前返回一个 Response,请求就终止了。常见错误是只在 if 分支里 return response(),却忘了 else 分支必须显式 return $next($request),否则函数末尾隐式返回 null,框架会当成中断处理(白屏或 500)。
- 正确写法:
if (!Auth::check()) { return redirect('/login'); } return $next($request); - 错误写法:
if (!Auth::check()) { abort(403); }(缺return $next($request),函数结束时返回null) - 别用
die()或exit(),会跳过框架响应生命周期,日志、钩子、缓存等全失效 - 推荐继承
think\middleware\BaseMiddleware,它自动处理类型提示和基础方法封装
为什么中间件注册了却不拦截?检查注册位置
全局注册(app/middleware.php)只对应用级请求生效,但控制器方法是否被拦截,取决于路由是否真正“经过”该中间件。比如你把 CheckAuth::class 放进全局数组,但某个接口走的是 CLI 命令或 API 网关直连,就可能绕过它。
- 控制器级拦截必须绑定到路由:如
Route::get('admin/user', 'Admin/UserController@index')->middleware('auth'); - 资源路由(
Route::resource())不会自动继承控制器注解或构造函数设置,需单独为每个动作指定中间件 - 若用分组路由,中间件要写在 group 内部:如
Route::group(['middleware' => 'auth'], function () { ... }); - 类名必须带完整命名空间(
app\middleware\Auth),而路由里简写的'auth'是靠配置映射的,别混淆
拦截后返回什么?按场景选响应方式
返回内容影响前端行为、日志记录和调试体验。不是所有 response() 都等价,尤其在 API 和页面跳转混合项目中。
- API 拒绝:用
json(['code' => 403, 'msg' => 'Forbidden']),并手动设Content-Type: application/json - 页面跳转:用
redirect('/login'),比response()->redirect()更兼容 Session 和 Referer - 需要触发统一异常处理(如写日志、上报监控):用
abort(403),它会进入框架异常流程,不是简单抛异常 - Mock 开发环境:仅当
APP_DEBUG === true且配置mock_enabled === true时才拦截,避免污染生产
前置拦截容易被忽略的三个硬性条件
很多拦截逻辑看似写了,上线后却无效,问题往往出在执行时机或环境适配上。
- Host 头校验类中间件(如
CheckHost)必须放在app/middleware.php数组最前面,否则路由已解析、控制器已初始化,再拦就晚了 - 跨域预检(
OPTIONS)必须由中间件主动响应 204,不能依赖控制器;且Cors::class必须置顶,否则SessionMiddleware可能已输出响应头导致 headers already sent - 带
credentials: include的请求,Access-Control-Allow-Origin不能为*,必须动态读$request->header('origin')并白名单校验
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











