render()是唯一能修改HTTP响应的地方,必须在App\Exceptions\Handler的render()方法中拦截异常并返回Response实例,如ModelNotFoundException返回404、AuthorizationException返回403;report()仅负责日志记录,不影响响应。

render() 是唯一能改 HTTP 响应的地方
想让某个异常返回特定状态码(比如 ModelNotFoundException 返回 404,AuthorizationException 返回 403),必须在 App\Exceptions\Handler.php 的 render() 方法里拦截处理。别碰 report() —— 它只管记录日志,不影响响应内容。
常见错误现象:在 report() 里写 response()->json(),结果页面还是默认调试页或空白;或者漏掉 return,导致执行完自定义逻辑后又走到 parent::render(),响应被覆盖。
- 所有分支都必须有
return,包括最后的parent::render($request, $exception) - 判断顺序很重要:先检查子类异常(如
InsufficientBalanceException),再检查父类(如Exception),否则会被父类兜底 - 不要在
render()里做 DB 查询、HTTP 调用等耗时操作,它在响应生成末期执行,会拖慢整个请求
如何为业务异常返回 JSON 并带自定义错误码
API 场景下,前端需要统一结构的 JSON 响应(比如 {"code": 1002, "message": "余额不足"}),而不是 HTML 页面。这时不能依赖 resources/views/errors/ 下的 Blade 模板,得在 render() 里用 response()->json() 显式构造。
关键点在于:自定义异常类要继承 Exception,并确保 getCode() 返回业务错误码(非 HTTP 状态码),getMessage() 返回用户提示语。
- 抛出时:
throw new InsufficientBalanceException("余额不足", 1002); - 拦截时:
if ($exception instanceof InsufficientBalanceException) { return response()->json(['code' => $exception->getCode(), 'message' => $exception->getMessage()], 400); } - 注意 HTTP 状态码(这里是
400)和业务错误码(1002)是两个东西,别混用
ValidationException 怎么提取 errors 字段并保持 422
手动抛出验证失败时,别直接 return response()->json(['errors' => [...] ])。Laravel 内置的 ValidationException 本身就携带 $exception->errors() 和正确的 422 状态码,复用它更可靠。
比如在深层方法里发现参数不合法,可以直接抛:throw ValidationException::withMessages(['email' => ['邮箱格式错误']]);。这样 render() 会自动走 Laravel 默认逻辑,返回标准 422 JSON 响应,字段结构和表单验证失败完全一致。
- 不用自己拼
errors数组层级,避免和框架默认格式不一致 - 如果需要额外上下文(比如 trace_id),可在抛出前设置
$exception->errorBag或扩展属性,但别动状态码逻辑 - 别用
abort(422)替代 —— 它不带errors字段,前端解析会失败
为什么 404 页面不显示自定义 Blade 模板
不是代码没写对,大概率是环境配置没切对。Laravel 只有在 APP_ENV=production 且 APP_DEBUG=false 时,才会从 resources/views/errors/ 加载 404.blade.php;开发环境下一律显示调试页。
测试时临时改 .env 后,记得清缓存:php artisan config:clear,否则配置不生效。另外,确保文件名严格匹配状态码,比如 404.blade.php,不是 404.php 或 notfound.blade.php。
-
NotFoundHttpException(路由未匹配)走的是视图机制,会查resources/views/errors/404.blade.php -
ModelNotFoundException默认不触发视图机制,必须在render()里显式response()->view('errors.404')才行 - Blade 模板里别用
{{ $exception->getMessage() }}直接输出异常信息,生产环境要隐藏敏感细节
最易忽略的一点:render() 方法签名在 Laravel 9+ 已改为接收 Throwable,不是 Exception,用 instanceof 判断时类型要对得上,否则条件永远不成立。


















