Laravel 自定义异常处理器:把异常收敛成统一 JSON 响应
test12026-10-2918 次阅读
为什么要统一异常出口
默认情况下,Laravel 在 debug 开时会吐出带栈的 HTML 错误页,关了又只给个 500。前后端分离的项目更需要一个结构稳定的 JSON 错误体:固定字段、可机的 code、可追踪的 traceId。本文重写异常处理器,把各类异常收敛成同一套响应。
一、定义统一错误结构
先约定响应形状,所有错误都走它,前端只认这一个格式。
// app/Exceptions/ApiException.php
namespace App\Exceptions;
use Exception;
class ApiException extends Exception
{
public function __construct(
string $message = '',
public int $status = 400,
public string $code = 'BAD_REQUEST',
) {
parent::__construct($message, $status);
}
}二、重写 Handler
Laravel 11 的异常处理集中在 bootstrap/app.php 里用 withExceptions 注册。把 ApiException 映射成 JSON,其他异常也兜底成统一结构,避免泄露内部细节。
// bootstrap/app.php
->withExceptions(function ($exceptions) {
$exceptions->respond(function ($response, $e) {
if (app()->environment('production') && $response->getStatusCode() === 500) {
return response()->json([
'code' => 'SERVER_ERROR',
'message' => '服务器开小差了,请稍后再试',
'traceId' => request()->header('X-Trace-Id', ''),
], 500);
}
return $response;
});
$exceptions->render(function (\App\Exceptions\ApiException $e) {
return response()->json([
'code' => $e->code,
'message' => $e->getMessage(),
'traceId' => request()->header('X-Trace-Id', ''),
], $e->status);
});
})三、在业务里直接抛出
控制器和业务层不再手动拼响应,抛一个语义化的异常即可,处理器替你收口。
use App\Exceptions\ApiException;
public function show(int $id)
{
$post = Post::find($id);
if (!$post) {
throw new ApiException('文章不存在', 404, 'POST_NOT_FOUND');
}
return response()->json(['data' => $post]);
}四、用中间件带上 traceId
在入口中间件里生成或透传 traceId,异常响应里回带,方便链路排查。
public function handle($request, $next)
{
$traceId = $request->header('X-Trace-Id') ?? (string) \Illuminate\Support\Str::uuid();
$request->headers->set('X-Trace-Id', $traceId);
$response = $next($request);
$response->headers->set('X-Trace-Id', $traceId);
return $response;
}小结
统一异常处理器让“出错”也变成一种契约:无论哪层抛错,前端拿到的都是同一套 code/message/traceId。开发期能精确定位,生产期不泄露栈,排障体验会好很多。
T
test1
文章作者
为什么要统一异常出口 默认情况下,Laravel 在 debug 开时会吐出带栈的 HTML 错误页,关了又只给个 50...
- 分类
- 技术
- 发布时间
- 2026-10-29
- 字数
- 约 2147 字
- 阅读
- 18 次
