全局异常处理器在 app\exceptions\handler 类中修改,所有未捕获异常均经其 render()(负责响应)和 report()(负责上报)方法处理,不应在中间件或 bootstrap/app.php 中兜底,以确保 laravel 请求上下文与服务容器完整可用。

全局异常处理器在哪改?
Laravel 的异常统一入口是 App\Exceptions\Handler 类,所有未被 try/catch 捕获的异常最终都会流经它的 render() 和 report() 方法。别去动 bootstrap/app.php 或中间件里加兜底逻辑——那不是 Laravel 的设计意图,反而容易绕过框架的上下文(比如 request、auth、log channel)。
-
report()负责“上报”:记录日志、发 Slack、调 Sentry SDK,但不返回响应 -
render()负责“响应”:决定返回 404 页面、JSON 错误结构,或重定向
改错方法:直接在 app/Exceptions/Handler.php 里调整这两个方法,而不是写个全局 set_exception_handler() ——那样会丢失 Laravel 的 request scope 和 service container 绑定。
怎么让业务异常也进 report()?
默认情况下,只有继承 Exception 的异常会被 report() 处理;但如果你抛的是 RuntimeException 或自定义异常类,必须确保它没被 $dontReport 数组排除。
- 检查
$dontReport属性:里面列的异常类(如ValidationException、NotFoundHttpException)不会进report() - 如果你写了
throw new MyBusinessException(),且希望它上报,就别把它加进$dontReport,也不要用new RuntimeException()冒充——最好显式继承Exception - 注意:Laravel 自带的
ValidationException和ModelNotFoundException默认不报,因为它们属于预期内的用户错误,不是系统级故障
示例:
protected $dontReport = [
\Illuminate\Validation\ValidationException::class,
\Illuminate\Auth\AuthenticationException::class,
// MyBusinessException 不要放这里
];
JSON API 场景下 render() 怎么统一格式?
前后端分离项目里,不能让 render() 直接返回 HTML 错误页。关键是识别请求是否为 API 请求,再决定响应格式。
- 判断依据不是
request()->is('api/*'),而是$request->expectsJson()或$request->wantsJson() - 不要覆盖整个
render()逻辑,优先用父类行为兜底:对非 JSON 请求仍走默认流程(比如 404 返回视图) - 常见错误:在
render()里硬编码response()->json(..., 500),结果把 404、422 等状态码全变成 500
正确做法:
public function render($request, Throwable $exception)
{
if ($request->expectsJson()) {
return response()->json([
'message' => $exception->getMessage(),
'error_code' => method_exists($exception, 'getCode') ? $exception->getCode() : 500,
], $this->getExceptionStatusCode($exception));
}
<pre class="brush:php;toolbar:false;">return parent::render($request, $exception);}
Sentry 上报时为什么丢了 Request ID 和 User ID?
Sentry 默认只捕获异常堆栈,不自动注入 Laravel 的上下文信息。必须手动绑定 request、user、session 等数据,否则排查时根本无法关联请求链路。
- 在
report()中调用 Sentry SDK 前,用\Sentry\configureScope()注入关键字段 - 不要在
boot()里一次性配置 scope——那是全局静态作用域,无法按请求隔离 - 容易漏掉:
auth()->user()可能为 null,得判空;request()->id()在 Laravel 9+ 才有,旧版本得用Str::uuid()->toString()自己生成并存到 request attribute
示例片段:
public function report(Throwable $exception)
{
if (app()->bound('sentry')) {
\Sentry\configureScope(function (\Sentry\State\Scope $scope) {
if (request()->has('id')) {
$scope->setTag('request_id', request()->id());
}
if (auth()->check()) {
$scope->setUser([
'id' => auth()->id(),
'email' => auth()->user()->email,
]);
}
});
}
<pre class="brush:php;toolbar:false;">parent::report($exception);}
异常处理真正难的不是接住错误,而是让每个错误都携带足够线索——request ID、用户身份、触发路径、中间件栈。少一个,排查时间就翻倍。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











