应分级处理异常:业务异常用warning日志,数据库错误取真实SQL信息,过滤敏感字段;API请求需用expectsJson()或路径判断返回JSON;事务中避免吞异常,禁用嵌套transaction;report()中第三方调用须设超时。

直接在 App\Exceptions\Handler 类里改 report() 和 render() 就行,但别一上来就全盘接管——多数框架内置异常(比如 ValidationException、ModelNotFoundException)已有合理处理逻辑,硬覆盖反而导致表单错误不显示、404变白屏。
怎么让自定义异常进日志但不打满屏幕
重点不是“捕获所有”,而是“只报告该报的”。生产环境里,report() 方法里应做类型判断和分级处理:
- 对预期业务异常(如
InsufficientBalanceException),用Log::warning()记录,不触发告警 - 对数据库底层错误(
QueryException),必须调用$exception->getPrevious()?->getMessage()拿真实 SQL 报错,否则日志里只有 “Integrity constraint violation” 这种废话 - 敏感信息过滤:检查
$exception->getTraceAsString()是否含密码、token 字段,有就str_replace()掉再记录 - 避免重复上报:Laravel 默认已调用
parent::report($exception),自己加逻辑前先确认是否需要绕过它
API 请求抛异常为啥返回 HTML 页面
因为 render() 没识别请求类型。默认行为是返回 500.blade.php,哪怕接口客户端明确要 JSON。
- 用
$request->expectsJson()判断是否为 API 请求(比检查Accept头更可靠) - 对
ModelNotFoundException,返回response()->json(['message' => 'Not found'], 404),不是 500 - 别依赖全局
APP_DEBUG控制输出格式——调试时想看堆栈,上线时要的是结构化错误,两者得分开处理 - 如果中间件里抛异常(比如权限校验失败),需显式判断并返回 JSON,因为中间件异常可能跳过路由层,
$request->expectsJson()在某些早期阶段不可用,建议改用$request->is('api/*')做路径前缀判断
事务里抛异常为啥没回滚
常见原因是异常被吞了,或事务没真正启动。
- 用
DB::transaction()闭包时,内部不能有try/catch——一旦捕获并静默处理,Laravel 就不知道该回滚 - 手动事务(
beginTransaction())必须配对commit()/rollback(),且catch块里不能漏掉DB::rollBack() - 嵌套
DB::transaction()无效:Laravel 不支持真嵌套,外层没异常,内层抛错也不会触发整体回滚 - 注意异常类型:
throw new Error()或致命错误(如内存超限)不会被DB::transaction()捕获,只能靠手动事务 +register_shutdown_function()补救
最易被忽略的一点:report() 里调用第三方服务(比如 Slack 通知)必须设超时和兜底,否则异常处理本身卡住,整个请求就 hang 死。不要把错误恢复逻辑做得比错误本身还脆弱。


















