在PHP项目中使用Guzzle客户端发起HTTP请求时,各类异常情况不可避免,比如目标服务不可达、请求超时、接口返回4xx或5xx状态码等,合理捕获这些异常并封装成统一的结构化错误信息,能让后续的错误排查和前端对接更加顺畅。

Guzzle常见异常类型
Guzzle在请求过程中会抛出多种异常,不同异常对应不同的错误场景,了解这些类型是精准捕获异常的基础:
- RequestException:所有请求相关异常的父类,大部分Guzzle抛出的异常都继承自这个类型,比如网络错误、响应状态码错误等。
- ConnectException:网络连接失败抛出的异常,比如目标服务器无法访问、DNS解析失败等场景会触发。
- ClientException:请求成功但服务端返回4xx状态码时抛出,对应客户端请求参数错误等问题。
- ServerException:请求成功但服务端返回5xx状态码时抛出,对应服务端内部错误等问题。
- TooManyRedirectsException:请求重定向次数超过配置的最大值时抛出的异常。
基础异常捕获实现
最基础的捕获方式是通过try-catch块包裹Guzzle请求逻辑,先捕获具体的异常类型,再兜底捕获通用的RequestException,避免遗漏未处理的异常:
<?php
require 'vendor/autoload.php';
use GuzzleHttpClient;
use GuzzleHttpExceptionClientException;
use GuzzleHttpExceptionConnectException;
use GuzzleHttpExceptionServerException;
use GuzzleHttpExceptionRequestException;
$client = new Client();
try {
// 发起GET请求
$response = $client->request('GET', 'https://api.ipipp.com/test');
// 请求成功,处理响应内容
$body = $response->getBody()->getContents();
echo "请求成功,响应内容:" . $body;
} catch (ConnectException $e) {
// 处理网络连接异常
echo "网络连接失败:" . $e->getMessage();
} catch (ClientException $e) {
// 处理4xx客户端错误
$response = $e->getResponse();
$statusCode = $response->getStatusCode();
$body = $response->getBody()->getContents();
echo "客户端请求错误,状态码:{$statusCode},响应内容:" . $body;
} catch (ServerException $e) {
// 处理5xx服务端错误
$response = $e->getResponse();
$statusCode = $response->getStatusCode();
echo "服务端内部错误,状态码:{$statusCode}";
} catch (RequestException $e) {
// 兜底捕获其他请求相关异常
echo "请求发生异常:" . $e->getMessage();
}
封装结构化错误信息
实际项目中通常需要返回统一格式的结构化错误信息,方便前端统一处理,我们可以定义一个错误结构,将异常信息、状态码、错误类型等内容封装进去:
<?php
require 'vendor/autoload.php';
use GuzzleHttpClient;
use GuzzleHttpExceptionClientException;
use GuzzleHttpExceptionConnectException;
use GuzzleHttpExceptionServerException;
use GuzzleHttpExceptionRequestException;
/**
* 发起Guzzle请求并返回结构化结果
* @param string $method 请求方法
* @param string $url 请求地址
* @param array $options 请求选项
* @return array 结构化结果数组
*/
function guzzleRequest(string $method, string $url, array $options = []): array
{
$client = new Client();
// 默认结构化返回格式
$result = [
'success' => false,
'code' => 0,
'message' => '',
'data' => null,
'error_type' => ''
];
try {
$response = $client->request($method, $url, $options);
$result['success'] = true;
$result['code'] = $response->getStatusCode();
$result['data'] = json_decode($response->getBody()->getContents(), true);
$result['message'] = '请求成功';
return $result;
} catch (ConnectException $e) {
$result['error_type'] = 'connect_error';
$result['message'] = '网络连接失败,请检查网络或目标服务地址';
$result['code'] = 503;
} catch (ClientException $e) {
$response = $e->getResponse();
$result['error_type'] = 'client_error';
$result['code'] = $response->getStatusCode();
$result['message'] = '客户端请求参数错误';
$result['data'] = json_decode($response->getBody()->getContents(), true);
} catch (ServerException $e) {
$response = $e->getResponse();
$result['error_type'] = 'server_error';
$result['code'] = $response->getStatusCode();
$result['message'] = '服务端处理请求时发生错误';
} catch (RequestException $e) {
$result['error_type'] = 'request_error';
$result['message'] = '请求发生未知错误:' . $e->getMessage();
$result['code'] = 500;
}
return $result;
}
// 调用示例
$res = guzzleRequest('GET', 'https://api.ipipp.com/user/info', [
'query' => ['id' => 1],
'timeout' => 5
]);
// 输出结果
echo json_encode($res, JSON_UNESCAPED_UNICODE);
配置项避免异常抛出
如果不需要Guzzle自动抛出异常,可以通过配置http_errors参数关闭默认的异常抛出行为,自行根据响应状态码判断错误:
<?php
require 'vendor/autoload.php';
use GuzzleHttpClient;
$client = new Client();
// 关闭自动抛出状态码异常
$response = $client->request('GET', 'https://api.ipipp.com/not/exist', [
'http_errors' => false,
'timeout' => 3
]);
$statusCode = $response->getStatusCode();
if ($statusCode >= 400) {
// 自行处理错误状态码
echo "请求失败,状态码:{$statusCode}";
} else {
echo "请求成功,响应内容:" . $response->getBody()->getContents();
}
注意事项
在处理异常时需要注意几个细节,避免信息泄露或者逻辑错误:
- 不要直接将异常的完整堆栈信息返回给前端,尤其是生产环境,避免暴露服务器路径、代码逻辑等敏感信息。
- 结构化错误信息的格式需要保持统一,比如固定包含success、code、message等字段,方便前端做统一的逻辑判断。
- 如果请求需要携带敏感信息,在记录错误日志时需要脱敏处理,避免敏感数据泄露。
- 超时时间、重试次数等配置需要根据实际业务场景调整,避免过长的等待时间影响接口性能。