应重写 handler 类的 render() 方法但保留框架默认异常处理,仅拦截业务异常;api 响应需统一 json 格式并准确设状态码;queryexception 日志需展开 getprevious() 获取详情;异步任务异常需监听 jobfailed 事件。

全局异常处理器怎么改才不影响框架默认行为
直接重写 App\Exceptions\Handler 类里的 render() 方法是最常见做法,但别一上来就 return 一个自定义响应——Laravel 默认对 ValidationException、NotFoundHttpException 等都做了友好处理,绕过它们会导致表单错误不显示、404 页面变白屏。
正确做法是先判断异常类型,只拦截你真要干预的:
- 用
instanceof区分业务异常(比如自定义的BusinessException)和框架异常 - 对
ValidationException,可调用$exception->errors()提取字段级错误,但别丢掉原response() - 生产环境禁止输出
$exception->getTraceAsString(),开发环境也建议限制在APP_DEBUG=true时才暴露
API 接口里怎么统一返回错误结构
Web 页面可以跳 404 或渲染错误页,但 API 必须返回 JSON 格式且状态码准确。别在每个控制器里手动 return response()->json([...], 400),容易漏状态码或格式不一致。
推荐在 Handler@render 中集中处理:
- 检查请求是否是 AJAX 或带
Accept: application/json,用$request->expectsJson()判断 - 对
ModelNotFoundException,返回404+{"message": "Resource not found"},不是500 - 捕获
QueryException时注意:MySQL 的1062 Duplicate entry是业务可预期错误,不该当 500 上报
日志里为什么看不到 SQL 错误详情
默认情况下,Laravel 把数据库异常包装成 QueryException,原始错误信息藏在 $exception->getPrevious() 里。直接记录 $exception->getMessage() 只会看到“SQLSTATE[23000]: Integrity constraint violation”,没具体字段和值。
想让日志可排查,得手动展开:
- 在
Handler@report中判断$exception instanceof QueryException - 用
$exception->getPrevious()?->getMessage()拿到底层驱动错误(如 PDO 的完整报错) - 注意不要把敏感参数(如密码、token)打到日志里;
DB::enableQueryLog()仅限本地调试,线上必须关
测试环境异常页面为啥不显示变量值
即使 APP_DEBUG=true,Laravel 9+ 默认用新的 Ignition 错误页,它默认隐藏闭包变量、大型数组和对象属性,避免泄露或卡死浏览器。
需要临时查看时,有三个实际可用的点:
- 点击错误页右上角「Show context」按钮展开当前作用域变量
- 在
config/app.php中设'debug_blacklist' => []清空屏蔽列表(仅限本地) - 更稳妥的方式是加一句
throw new \Exception('Debug: ' . json_encode($someVar, 320));强制触发并查看
真正难处理的是异步任务里的异常——队列失败不会进 Handler@report,得单独监听 JobFailed 事件。









