必须显式返回think\Response实例才能中断请求,仅调用response()不return或返回非Response对象均无效;正确写法是return response()->json([])->code(403)。

中间件里return response()为什么没中断请求?
因为只调用了 response() 但没 return,或者返回的是非 Response 实例。ThinkPHP 的管道机制只在中间件函数「显式返回」一个 think\Response 对象时,才会跳过后续中间件和控制器。
常见错误写法:
-
response()->json(['msg'=>'forbidden']); return;→ 没返回值,框架继续走 -
echo json_encode([]); exit;→ 绕过框架,Header、日志、事件全丢 -
return $next($request);放在条件分支外 → 总会执行到控制器
正确姿势是:必须 return 一个由 response() 或 json() 等助手函数创建的 Response 实例。
如何在中间件中返回带状态码的 JSON 响应?
用 response()->json() 配合 code() 方法最直接,它能同时设置 HTTP 状态码和响应体,且不依赖请求类型(AJAX/非 AJAX)。
立即学习“PHP免费学习笔记(深入)”;
示例(权限校验失败):
return response()->json([
'code' => 403,
'msg' => '无访问权限',
'data' => []
])->code(403);
注意点:
-
code(403)是设置 HTTP 状态码,不是响应体里的code字段 - 别漏掉
return,否则等于没写 - 不要用
http_response_code(403)单独设状态码——它不会中断流程 - 如果用
json()辅助函数(TP6.0+),它等价于response()->json(),但需确认版本是否支持链式code()
为什么返回了 Response 还进了控制器?注册位置不对
中间件是否生效,取决于它被注册在哪个层级:全局中间件 return response() 却仍进了控制器,大概率是路由定义里又加了一层中间件,而它在管道中排得更靠后,覆盖了前面的中断逻辑。
检查方式:
- 看
app/middleware.php里是否注册为全局中间件(middleware数组) - 查路由定义,比如
Route::get('api/user', 'User')->middleware('auth');—— 这个auth会比全局中间件晚执行 - 控制器方法上是否有
@middleware注解?它优先级最低,但一旦存在,就可能“补位”执行
真正想拦截所有请求,应把中间件注册在全局,并确保它在 middleware.php 中位于靠前位置(顺序影响执行先后)。
开发环境 Mock 数据怎么安全拦截不污染生产?
核心是环境开关 + 路径匹配 + 手动设头。只在 APP_DEBUG === true 且配置项 mock_enabled === true 时才启用,其他情况直接放行。
关键细节:
- 用
$request->url()匹配完整 URL(含 query),比$request->path()更准,避免误伤 - 返回前必须手动
header('Content-Type', 'application/json'),否则前端fetch().json()会因 MIME 类型不匹配而失败 - 别用
view()或redirect(),Mock 场景下只返回 JSON 数据即可 - Mock 数据建议从配置或独立文件读取,不要硬编码在中间件里,便于后期替换
最易被忽略的一点:TP6 的中间件在路由解析前执行,所以不能依赖 Route::rule() 或 Route::current() —— 它们此时还没初始化。



















