不可行——中间件无法覆盖异常场景。正常响应需用中间件包装$data,异常响应必须在handler::render()中单独处理apiexception并返回json,二者缺一不可。

不可行——至少不能只靠中间件覆盖全部返回场景。
中间件 handle 方法只能拦截正常流程,拦不住异常
当你在 handle 里调用 $next($request) 后拿到 $response,再统一包装结构,这仅对控制器成功返回值有效。但一旦控制器抛出 ApiException 或其他未捕获异常,Laravel 会跳过后续中间件,直接交由 App\Exceptions\Handler::render() 处理——此时你的中间件根本不会被执行。
- 常见错误现象:前端收不到
{"code":400,"message":"xxx"},而是得到 Laravel 默认的 500 HTML 页面或裸异常堆栈 - 原因就是异常没进中间件,而是在
render()阶段被截断了 - 官方明确说明:从 Laravel 5.2 起,异常处理逻辑与中间件是分离的,不推荐在中间件里写
try/catch
统一返回必须分两路:正常流走中间件,异常流走 ExceptionHandler
真正能落地的方案是双轨制,缺一不可:
- 正常响应:用一个中间件(如
FormatApiResponse)在$next($request)后修改$response->getData(),套上code/message/data结构 - 异常响应:在
app/Exceptions/Handler.php的render()方法里判断$e instanceof ApiException,手动构造 JSON 响应并返回response()->json(...) - 注意:
render()中不要调用parent::render(),否则可能触发默认 HTML 渲染;若需 fallback,应显式判断异常类型再委托
中间件传参对统一返回没帮助,别误用
有人试图用 middleware('format:api') 这类带参数的方式控制格式化行为,但这和统一返回无关——参数只是用来做条件分支(比如区分 API / Web 响应格式),并不能解决“异常绕过中间件”这个根本限制。
- 传参后你依然得在
handle()里写相同的包装逻辑,该漏的异常还是漏 - 参数本身是字符串数组,比如
['api'],不提供任何异常捕获能力 - 滥用参数反而增加路由配置复杂度,且无法覆盖全局所有接口
最易被忽略的一点:中间件返回的 $response 必须是 Illuminate\Http\Response 实例,如果控制器返回的是数组或视图,$response->getData() 可能为 null 或原始值,直接 setData() 会失败——得先判断响应类型再处理。











