必须开启错误显示并记录完整请求响应上下文:在web/index.php开头加error_reporting(e_all)和ini_set('display_errors', '1'),启用yii_debug和yii_env='dev',配置php.ini中display_errors=on、log_errors=on;使用guzzle时设http_errors=>false再判断状态码,curl后检查curl_errno,yii2 httpclient需手动校验isok;敏感信息须脱敏后记录到runtime/logs/app.log;控制器中应显式捕获并返回结构化错误,避免异常进入全局errorhandler。

Yii2 调用第三方 API 报错时,默认不会把原始响应或异常细节透出,尤其在生产环境常表现为 500 空白页、curl_exec 返回 false、或 Guzzle 抛出 RequestException 但没日志——这不是代码没写 try/catch,而是错误被静默吞掉或没落到可观察层。
打开调试并强制显示 PHP 错误
这是所有后续排查的前提。若连真实报错都看不到,就只能靠猜:
- 在
web/index.php开头插入两行:error_reporting(E_ALL); ini_set('display_errors', '1'); - 确认
defined('YII_DEBUG') or define('YII_DEBUG', true);和YII_ENV = 'dev'已启用 - 如果用的是 Nginx + PHP-FPM,还要检查
php.ini中display_errors = On和log_errors = On是否生效(仅改index.php不够)
捕获并打印第三方请求异常
别依赖 try { $response = $client->get(...); } catch (\Exception $e) { } 就完事——很多底层错误(如 DNS 失败、SSL 握手超时、连接被重置)会抛出不同类的异常,Guzzle 默认只 throw RequestException,但 cURL 错误码需手动查 $e->getResponse() 或 $e->getHandlerContext():
- 用 Guzzle 时,加
http_errors => false参数避免自动 throw,先拿到响应再判断:$response = $client->get($url, ['http_errors' => false]); if ($response->getStatusCode() >= 400) { var_dump($response->getBody()->getContents()); } - 用原生
cURL时,在curl_exec()后立刻检查:if (curl_errno($ch)) { error_log('cURL error ' . curl_errno($ch) . ': ' . curl_error($ch)); } - 若用 Yii2 自带的
yii\httpclient\Client,它默认不抛异常,得手动检查:$response = $client->get($url)->send(); if (!$response->isOk) { var_dump($response->content, $response->statusCode); }
记录完整请求/响应上下文到日志
仅靠 var_dump 无法复现线上问题,必须落盘:
- 在调用前记录请求参数:
Yii::info(['url' => $url, 'headers' => $headers, 'body' => $body], 'thirdparty-api-request'); - 在响应后记录关键字段:
Yii::info(['status' => $response->getStatusCode(), 'headers' => $response->getHeaders()], 'thirdparty-api-response'); - 敏感字段(如 token、手机号)要脱敏,否则日志泄露风险极大;可用
str_replace或正则擦除,别直接print_r($all) - 注意:日志文件路径在
runtime/logs/app.log,确保该目录可写,且磁盘没满(满则日志静默失败)
绕过 Yii2 ErrorHandler 对第三方异常的拦截
Yii2 的 yii\web\ErrorHandler 会接管所有未捕获异常,包括你在 API 调用中主动 throw new Exception() 的场景——但它默认渲染成 HTML 页面,而你想要的是 JSON 响应或原始错误信息:
- 在控制器 action 中,不要让异常漏到全局 handler;显式 catch 并返回:
return ['code' => 500, 'message' => '第三方服务不可用', 'detail' => $e->getMessage()]; - 若用的是 REST 控制器,可在
behaviors()中临时禁用'class' => 'yii\filters\ContentNegotiator'的自动格式化,避免 JSON 包裹里混入 HTML 标签 - 最关键一点:别在
catch块里再throw $e,否则二次进入ErrorHandler,可能触发死循环或掩盖原始堆栈
真正难的不是捕获异常,而是区分哪些是网络层错误(DNS、TLS、超时)、哪些是业务层错误(HTTP 4xx/5xx、JSON 解析失败)、哪些是第三方接口文档没写的隐式限制(比如频率控制返回 200 但 body 是错误文本)。每次报错,先看 runtime/logs/app.log 里有没有 cURL error 或 Connection refused,再查响应体是否为空或含 "error": 字段——顺序错了,排查效率直接打五折。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











