全局异常捕获需精准干预:保留ValidationException、ModelNotFoundException等默认处理,仅定制BusinessException等业务异常;API请求用expectsJson()判断并统一JSON格式;QueryException需获取底层PDO错误并脱敏;日志按影响分级,异步任务单独监听JobFailed事件。

直接改 App\Exceptions\Handler 就能实现全局异常捕获,但关键不是“全拦”,而是“精准干预”——保留框架对 ValidationException、ModelNotFoundException 等的默认友好处理,只接管你真正需要定制的业务异常和 API 错误响应。
只拦截该拦的异常类型
在 render() 方法里用 instanceof 做类型判断,避免一锅端:
- 对
BusinessException、PermissionException等自定义业务异常,返回结构化 JSON 或跳转提示页 - 对
ValidationException,调用$exception->errors()提取字段错误,再包装成统一格式,别丢掉原逻辑 - 对
ModelNotFoundException,返回 404 视图或response()->json(['message' => 'Not found'], 404),不是 500 - 让其他异常(如
SyntaxError、NotFoundHttpException)走parent::render(),确保表单报错不消失、404 页面正常渲染
API 请求必须识别并返回 JSON
不能依赖 Accept 头,用 Laravel 内置方法更可靠:
- 用
$request->expectsJson()判断是否为 API 请求(自动识别 X-Requested-With、Accept 等) - 若路径固定(如
/api/*),可用$request->is('api/*')作为兜底,尤其在中间件中避免expectsJson()不可用的情况 - 统一返回格式示例:
response()->json(['success' => false, 'message' => $exception->getMessage(), 'data' => []], 400)
数据库异常要挖到底层信息
QueryException 只是外壳,真实错误藏在上一层:
- 在
report()方法中判断$exception instanceof QueryException - 用
$exception->getPrevious()?->getMessage()获取原始 PDO/MySQL 报错,比如 “Duplicate entry 'xxx' for key 'users_email_unique'” - 记录前过滤敏感内容:检查消息或堆栈是否含
password、token、auth_key等字段,做脱敏替换 - 生产环境禁用
DB::enableQueryLog(),仅限本地调试开启
日志分级与环境隔离
不是所有异常都值得记 error 级别,得按影响分层:
- 业务可预期异常(如余额不足、库存告罄)用
Log::warning(),不触发告警 - 未预期错误(如空指针、配置缺失)才用
Log::error() - 开发环境(
APP_DEBUG=true)允许显示完整堆栈;生产环境一律关闭详细输出,靠日志+监控定位 - 异步任务异常需监听
JobFailed事件,单独处理重试与通知,别指望report()全覆盖


















