如何高效处理Laravel框架中的错误和异常?
- 内容介绍
- 文章标签
- 相关推荐
本文共计651个文字,预计阅读时间需要3分钟。
直接改写如下:
怎么在 render() 里区分网页请求和 API 请求
Laravel 默认对两者返回不同响应,但你得主动判断,不能只靠 $request->isJson() —— 它不可靠,很多前端发 JSON 时没带 Content-Type: application/json。
- 用
$request->expectsJson()才是正确姿势,它同时检查Accept头、X-Requested-With和请求路径是否含/api/ - 网页请求走 Blade 错误页(如
resources/views/errors/404.blade.php),API 请求必须返回标准 JSON 结构 + 正确 HTTP 状态码 - 别在
render()里调用dd()或抛新异常,否则可能触发递归崩溃 - 对
ModelNotFoundException,网页返回 404 视图,API 返回response()->json(['message' => 'Not found'], 404)
QueryException 的错误信息为什么总显示 “Integrity constraint violation”
因为 Laravel 把原始数据库错误包在了 $exception->getPrevious() 里,直接打 $exception->getMessage() 只能看到通用提示,查不到具体字段和值。
- 在
report()中加判断:if ($exception instanceof QueryException && $previous = $exception->getPrevious()) { \Log::error('DB error detail', ['raw' => $previous->getMessage()]); } - MySQL 错误码 1062(重复键)、1452(外键约束)属于业务可预期错误,不该当 500 上报,应提前捕获并转为 400 或自定义异常
- 线上环境禁止开启
DB::enableQueryLog(),它会吃内存且泄露 SQL 参数
自定义异常类该不该写进 $dontReport
该,但只针对真正“不值得记日志”的异常,比如表单验证失败、权限拒绝、资源未找到——它们高频、可预期、有明确语义。
- 在
Handler的register()方法里添加:$this->dontReport([ValidationException::class, AuthorizationException::class, ModelNotFoundException::class]); - 自己写的
InsufficientBalanceException这类业务异常,如果需要监控,就别放$dontReport;如果只是流程跳转用,可以加 -
$dontReport不影响render()行为,只跳过report()调用,所以仍需在render()中处理它的展示逻辑
最容易被忽略的是:生产环境 APP_DEBUG=false 时,$exception 变量在 500 视图里依然可用,但 $exception->getTraceAsString() 会被静默截断——别指望靠它现场 debug,得靠 report() 里记录的完整上下文。
本文共计651个文字,预计阅读时间需要3分钟。
直接改写如下:
怎么在 render() 里区分网页请求和 API 请求
Laravel 默认对两者返回不同响应,但你得主动判断,不能只靠 $request->isJson() —— 它不可靠,很多前端发 JSON 时没带 Content-Type: application/json。
- 用
$request->expectsJson()才是正确姿势,它同时检查Accept头、X-Requested-With和请求路径是否含/api/ - 网页请求走 Blade 错误页(如
resources/views/errors/404.blade.php),API 请求必须返回标准 JSON 结构 + 正确 HTTP 状态码 - 别在
render()里调用dd()或抛新异常,否则可能触发递归崩溃 - 对
ModelNotFoundException,网页返回 404 视图,API 返回response()->json(['message' => 'Not found'], 404)
QueryException 的错误信息为什么总显示 “Integrity constraint violation”
因为 Laravel 把原始数据库错误包在了 $exception->getPrevious() 里,直接打 $exception->getMessage() 只能看到通用提示,查不到具体字段和值。
- 在
report()中加判断:if ($exception instanceof QueryException && $previous = $exception->getPrevious()) { \Log::error('DB error detail', ['raw' => $previous->getMessage()]); } - MySQL 错误码 1062(重复键)、1452(外键约束)属于业务可预期错误,不该当 500 上报,应提前捕获并转为 400 或自定义异常
- 线上环境禁止开启
DB::enableQueryLog(),它会吃内存且泄露 SQL 参数
自定义异常类该不该写进 $dontReport
该,但只针对真正“不值得记日志”的异常,比如表单验证失败、权限拒绝、资源未找到——它们高频、可预期、有明确语义。
- 在
Handler的register()方法里添加:$this->dontReport([ValidationException::class, AuthorizationException::class, ModelNotFoundException::class]); - 自己写的
InsufficientBalanceException这类业务异常,如果需要监控,就别放$dontReport;如果只是流程跳转用,可以加 -
$dontReport不影响render()行为,只跳过report()调用,所以仍需在render()中处理它的展示逻辑
最容易被忽略的是:生产环境 APP_DEBUG=false 时,$exception 变量在 500 视图里依然可用,但 $exception->getTraceAsString() 会被静默截断——别指望靠它现场 debug,得靠 report() 里记录的完整上下文。

