关键在于用$request->expectsJson()或$request->is('api/*')判断API请求,优先复用parent::render(),再针对ModelNotFoundException、AuthenticationException等特定异常返回标准化JSON响应,并确保Content-Type正确。

如何在 Laravel 中区分 API 和网页请求做异常响应
关键不是重写整个 render() 方法,而是利用 Laravel 自带的 $request->expectsJson() 判断逻辑。Laravel 的 App\Exceptions\Handler 默认已对 AJAX/JSON 请求做了基础处理,但常被忽略的是:它只在 Accept: application/json 存在或请求是 XHR 时才触发,而很多 API 客户端(如 Postman、curl)不带该 header,导致走网页错误页流程。
实操建议:
- 不要删掉父类
parent::render($request, $exception),它内部已封装了expectsJson()+ 状态码映射逻辑 - 若需强制对某类请求返回 JSON(比如所有
/api/*路由),可在render()中提前拦截:if ($request->is('api/*') || $request->header('X-Requested-With') === 'XMLHttpRequest') { return response()->json([ 'message' => $exception->getMessage(), 'error' => class_basename($exception), ], $this->getStatusCode($exception)); } - 注意:Laravel 9+ 中
$this->getStatusCode($exception)是私有方法,应改用ResponseFactory::status()或直接查$exception->getStatusCode()(仅限HttpException及子类)
自定义 404 和 401 错误的 JSON 格式
默认的 NotFoundHttpException 和 AuthenticationException 渲染结果字段不统一,前端难处理。与其全局替换,不如针对具体异常类型定制。
实操建议:
- 在
App\Exceptions\Handler::render()中用instanceof匹配:if ($exception instanceof NotFoundHttpException) { return response()->json(['error' => 'resource_not_found'], 404); } if ($exception instanceof AuthenticationException) { return response()->json(['error' => 'unauthenticated'], 401); } - 避免硬编码状态码,优先调用
$exception->getStatusCode()(部分异常支持),否则 fallback 到固定值 - 别在
AuthenticationException处理中返回敏感信息(如message),生产环境应始终返回泛化提示
捕获模型未找到异常并转为 404 JSON
ModelNotFoundException 默认渲染成 HTML 500 页面,但 API 场景下应统一为 404 JSON。它和 NotFoundHttpException 不同,不会被 Laravel 默认 JSON 化。
实操建议:
- 在
render()中显式判断:if ($exception instanceof ModelNotFoundException) { return response()->json(['error' => 'record_not_found'], 404); } - 更稳妥的方式是配合路由模型绑定,在控制器中用
findOrFail()并 try-catch,但违背了“异常即流程控制”的 Laravel 风格 - 注意:Laravel 10+ 引入了
Illuminate\Database\Eloquent\ModelNotFoundException全限定名,确保 use 正确,否则 instanceof 不生效
开发与生产环境的 JSON 错误细节控制
本地调试需要堆栈和消息,线上必须隐藏。很多人直接在 render() 里写 app()->environment('local'),但 Laravel 已提供 $this->isDebugMode()(Laravel 9+)或更底层的 config('app.debug')。
实操建议:
- 构造 JSON 响应时,只在 debug 模式下暴露
trace和exception字段:$data = ['error' => $exception->getMessage()]; if (config('app.debug')) { $data['trace'] = $exception->getTraceAsString(); $data['exception'] = get_class($exception); } return response()->json($data, $status); - 不要把
$exception->__toString()直接塞进 JSON,可能含 HTML 实体或换行符,破坏 JSON 结构 - 如果用了第三方异常追踪服务(如 Sentry),记得在
report()方法里上报,而非render()—— 后者只管输出,不该承担日志职责
真正容易被忽略的是:API 异常响应的 Content-Type 必须是 application/json,而不仅仅是返回 JSON 字符串。Laravel 的 response()->json() 会自动设 header,但如果手写 response(json_encode(...)) 就得自己补 ->header('Content-Type', 'application/json')。


















