ThinkPHP中间件必须使用handle()方法且每个分支显式return,否则返回null导致请求静默中断;正确写法为前置拦截return redirect()/abort()或放行return $next($request),后置处理则先调用再操作response并return。

中间件必须用 handle() 方法,且每个分支都要显式 return
ThinkPHP5.1 中间件不生效,80% 是因为 handle() 函数里漏了 return。框架不会帮你补默认返回,没 return 就等于返回 null,请求直接中断,连 404 都不报,只留空白页或 500。
正确结构只有两种写法:
- 前置判断:逻辑在
$next($request)前,拦截时return redirect()或abort(403);放行时return $next($request) - 后置处理:先
$response = $next($request),再操作$response,最后return $response
别写成这样:if (!Auth::check()) { abort(403); } —— 缺少 else { return $next($request); },函数末尾无返回,必挂。
路由级绑定比全局注册更精准,别滥用 app/middleware.php
全局中间件(app/middleware.php)会作用于所有请求,包括 /static/js/app.js、/favicon.ico、登录接口,容易误伤或拖慢静态资源。
立即学习“PHP免费学习笔记(深入)”;
真正要控制权限、日志、参数校验,应该绑定到具体路由:
Route::get('admin/user', 'Admin/UserController@index')->middleware('CheckAuth');-
Route::resource('post', 'PostController')->middleware(['CheckAuth'], ['destroy']);(只对destroy操作生效) - 多个中间件按顺序执行:
->middleware(['CheckAuth', 'LogRequest'])
注意:路由里写的 'CheckAuth' 必须在 config/middleware.php 中有对应别名映射,否则要写完整类名 app\http\middleware\CheckAuth::class。
用 $request->method() 判断请求类型,别信 $_SERVER 或 isDelete()
想拦截 PUT/DELETE 等非 GET/POST 请求,不能依赖 $_SERVER['REQUEST_METHOD'] —— 在 CLI、Nginx 反向代理、CORS 预检(OPTIONS)下它不可靠;也别用 $request->isDelete(),该方法在中间件阶段可能未就绪。
唯一安全方式是:if ($request->method() === 'DELETE') 或 in_array($request->method(), ['PUT', 'PATCH', 'DELETE'])。
额外注意两点:
- 原生 DELETE 请求的 body 数据不在
$request->param()里,得用$request->input()读 raw body - 遇到 OPTIONS 预检请求,务必放行:
if ($request->method() === 'OPTIONS') { return $next($request); },否则后续 DELETE/PUT 永远发不出去
中间件里取不到用户信息?优先用 think\facade\Auth,别直查数据库
在中间件里写 Db::name('user')->where('id', $id)->find() 是典型错误:绕过认证态、重复连接、丢失缓存、无法响应登出状态。
正确做法是依赖框架已有的认证门面:
-
Auth::check()→ 是否已登录 -
Auth::id()→ 当前用户 ID(登录后才有) -
Auth::user()→ 完整用户模型,含角色、权限字段
如果权限逻辑复杂(如 RBAC),建议把权限树预加载进 Session 或 Redis,而不是每次请求都查库。另外,Auth::guest() 返回 false 不代表一定能进——还要看中间件是否真放行了 $next($request),这是最容易被忽略的链路断点。



















