// +---------------------------------------------------------------------- namespace ywxapp\handler; use Throwable; use think\Response; use think\exception\Handle; use think\exception\HttpException; use think\exception\ValidateException; use think\exception\HttpResponseException; use think\db\exception\DataNotFoundException; use think\db\exception\ModelNotFoundException; /** * 应用异常处理类. */ class ExceptionHandle extends Handle { /** * 不需要记录信息(日志)的异常类列表. * * @var array */ protected $ignoreReport = [ HttpException::class, HttpResponseException::class, ModelNotFoundException::class, DataNotFoundException::class, ValidateException::class, ]; /** * 记录异常信息(包括日志或者其它方式记录). * * @param Throwable $exception * * @return void */ public function report(Throwable $exception): void { event('app_exception_report', ['exception'=>$exception,'ignoreReport'=>$this->ignoreReport]); // 使用内置的方式记录异常日志 parent::report($exception); } /** * Render an exception into an HTTP response. * * @param \think\Request $request * @param Throwable $e * * @return Response */ public function render($request, Throwable $e): Response { event('app_exception', $e); // 显式响应异常($this->result->error() 等主动抛出)原样返回 if ($e instanceof HttpResponseException) { return $e->getResponse(); } // API / Ajax 请求:统一 JSON 输出;生产环境隐藏内部异常细节 if ($this->shouldReturnJson($request)) { $httpCode = ($e instanceof HttpException && $e->getStatusCode()) ? $e->getStatusCode() : 500; $isDebug = $this->app->isDebug(); $payload = [ 'code' => $httpCode, 'message' => $isDebug ? $e->getMessage() : '系统繁忙,请稍后重试', 'timestamp' => time(), ]; if ($isDebug) { $payload['debug'] = [ 'exception' => get_class($e), 'file' => $e->getFile() . ':' . $e->getLine(), 'trace' => explode("\n", $e->getTraceAsString()), ]; } return Response::create($payload, 'json', $httpCode); } // 其他错误(页面请求)交给系统处理 return parent::render($request, $e); } /** * 是否应以 JSON 输出异常:api 应用、Ajax 请求, * 或应用未解析时按 pathinfo 前缀兜底。 */ protected function shouldReturnJson($request): bool { if ($request->isAjax()) { return true; } if ($this->app->http->getName() === 'api') { return true; } $path = strtolower($request->pathinfo()); return str_starts_with($path, 'api/') || $path === 'api'; } }