laravel异常处理核心在app\exceptions\handler类,render()决定响应内容,report()仅负责日志;自定义错误页面需配合环境配置(app_debug=false)、异常类型判断、状态码映射及视图路径准确才生效。

直接结论:不要在 render() 里硬编码判断 APP_DEBUG,而应利用 Laravel 已有的环境感知机制 + 异常类型分支做响应分离。
为什么不能直接 if (app()->environment('production')) { ... } 在 render 中?
看似合理,但会破坏 Laravel 对调试页面的原生支持逻辑。比如开发时触发 ValidationException,你手动返回 JSON,就再也看不到 Whoops 的漂亮堆栈和变量快照了;又或者误判了 HttpException 子类(如 NotFoundHttpException),导致本该走 404 视图的请求被塞进通用 JSON 错误里。
正确做法是让环境配置驱动行为,而不是覆盖行为:
-
APP_DEBUG=true时,Laravel 默认用Whoops\Handler\PrettyPageHandler渲染,你只需确保没在render()里无条件 return 响应 -
APP_DEBUG=false时,才真正需要你介入定制输出格式 - 所有自定义逻辑应包裹在
if (! app()->isLocal() && ! config('app.debug'))条件下,避免本地调试失效
render 方法中区分 API 和 Web 请求的响应格式
同一个异常,在 AJAX 请求里返回 JSON,在普通页面请求里返回 Blade 视图,这是最常见也最容易出错的点。
别依赖 $request->expectsJson() —— 它只检测 Accept: application/json 或 X-Requested-With: XMLHttpRequest,而现代前端(如 Vue Router、React Router)常不带这些头。
更可靠的方式是结合路由命名或中间件标记:
- 给 API 路由加前缀(如
api/)或使用middleware('api'),然后在render()中用$request->route()?->getName()或$request->route()?->getPrefix()判断 - 对
Illuminate\Validation\ValidationException,统一用response()->json(..., 422),不管请求来源 —— 这是 RESTful 接口的事实标准 - 对
ModelNotFoundException,Web 请求返回response()->view('errors.404', [], 404),API 请求返回response()->json(['message' => 'Not found'], 404)
避免在生产环境暴露敏感信息的三个硬性检查点
哪怕 APP_DEBUG=false,仍可能因疏忽泄露路径、数据库配置、用户 token 等。
必须检查以下三处:
-
render()中调用$exception->getTraceAsString()前,先截断:substr($exception->getTraceAsString(), 0, 1000)—— 否则整条 trace 可能含.env路径或 SQL 查询原文 - 自定义 JSON 响应中,永远不要包含
$exception->getFile()和$exception->getLine()字段,除非你明确知道生产日志已脱敏且前端不渲染这些字段 - 如果用了第三方异常监控(如 Sentry),确认
report()方法里没把$request->all()或$request->cookies直接传过去 —— 应该用$this->context()显式控制上报字段
真正生效前必须验证的两件事
改完 app/Exceptions/Handler.php 后,光跑 php artisan serve 是不够的。
必须做这两步验证:
- 临时设
APP_ENV=production和APP_DEBUG=false,用curl -I http://localhost:8000/nonexistent看响应头是否为404且内容是你写的视图,不是默认白屏 - 在 API 路由里主动 throw new
RuntimeException('test'),用 Postman 发请求,确认返回的是 JSON 且不含file/line字段
漏掉任意一步,上线后都可能面对用户看到空白页或完整堆栈 —— 尤其是那个 APP_DEBUG=false 却没清缓存的 case,php artisan config:clear 必须跟在环境变量修改之后。











