gokure / http-client
围绕 Guzzle HTTP 客户端的 Hyperf 的一个表达性、最小化 API。
Requires
- php: >= 7.2
- ext-swoole: >= 4.4
- hyperf/framework: ^1.0|^2.0
- hyperf/guzzle: ^1.0|^2.0
Requires (Dev)
- hyperf/config: ^1.0|^2.0
- hyperf/di: ^1.0|^2.0
- hyperf/testing: ^1.0|^2.0
- mockery/mockery: ^1.0
- roave/security-advisories: dev-master
- swoole/ide-helper: ^4.5
- symfony/var-dumper: ^5.0
README
通过 laravel/framework 实现 Hyperf 的 HTTP 客户端。
介绍
HTTP 客户端是一个围绕 Guzzle HTTP 客户端(来自 Laravel)的表达性、最小化 API,允许您快速发出 HTTP 请求以与其他 Web 应用程序进行通信。
在开始之前,您应确保已将 Guzzle 包作为应用程序的依赖项安装。默认情况下,Hyperf 自动包含此依赖项。但是,如果您之前已移除该包,您可以通过 Composer 再次安装它。
composer require guzzlehttp/guzzle
安装
在您的 composer.json 中要求 gokure/http-client 包并更新您的依赖项
composer require gokure/http-client
发出请求
要发出请求,您可以使用 Http 门面提供的 get、post、put、patch 和 delete 方法。首先,让我们看看如何向另一个 URL 发出基本的 GET 请求
use Gokure\Http\Client\Http; $response = Http::get('http://example.com');
get 方法返回一个 Gokure\Http\Client 实例,该实例提供各种方法,可用于检查响应
$response->body() : string; $response->json() : array|mixed; $response->collect() : Hyperf\Utils\Collection; $response->status() : int; $response->ok() : bool; $response->successful() : bool; $response->failed() : bool; $response->serverError() : bool; $response->clientError() : bool; $response->header($header) : string; $response->headers() : array;
Gokure\Http\Client\Response 对象还实现了 PHP 的 ArrayAccess 接口,允许您直接在响应上访问 JSON 响应数据
return Http::get('http://example.com/users/1')['name'];
请求转储
如果您希望在发送之前转储发出请求的实例并终止脚本的执行,您可以在请求定义的开始处添加 dd 方法
return Http::dd()->get('http://example.com');
请求数据
当然,在发出 POST、PUT 和 PATCH 请求时发送额外的数据是很常见的,因此这些方法接受一个数组作为它们的第二个参数。默认情况下,数据将使用 application/json 内容类型发送
use Gokure\Http\Client\Http; $response = Http::post('http://example.com/users', [ 'name' => 'Steve', 'role' => 'Network Administrator', ]);
GET 请求查询参数
在发出 GET 请求时,您可以直接将查询字符串附加到 URL 上,或者将键/值对的数组作为 get 方法的第二个参数传递
$response = Http::get('http://example.com/users', [ 'name' => 'Taylor', 'page' => 1, ]);
发送表单 URL 编码请求
如果您想使用 application/x-www-form-urlencoded 内容类型发送数据,您应该在发出请求之前调用 asForm 方法
$response = Http::asForm()->post('http://example.com/users', [ 'name' => 'Sara', 'role' => 'Privacy Consultant', ]);
发送原始请求体
如果您在发出请求时想提供原始请求体,您可以使用 withBody 方法。内容类型可以通过方法的第二个参数提供
$response = Http::withBody( base64_encode($photo), 'image/jpeg' )->post('http://example.com/photo');
多部分请求
如果您想以多部分请求发送文件,您应在发送请求之前调用attach方法。此方法接受文件的名称及其内容。如果需要,您还可以提供一个第三个参数,该参数将被视为文件的名称
$response = Http::attach( 'attachment', file_get_contents('photo.jpg'), 'photo.jpg' )->post('http://example.com/attachments');
您可以选择传递一个流资源而不是文件的原始内容
$photo = fopen('photo.jpg', 'r'); $response = Http::attach( 'attachment', $photo, 'photo.jpg' )->post('http://example.com/attachments');
头部信息
可以使用withHeaders方法向请求添加头部信息。此withHeaders方法接受一个键/值对的数组
$response = Http::withHeaders([ 'X-First' => 'foo', 'X-Second' => 'bar' ])->post('http://example.com/users', [ 'name' => 'Taylor', ]);
认证
您可以使用withBasicAuth和withDigestAuth方法分别指定基本和摘要认证凭据
// Basic authentication... $response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(...); // Digest authentication... $response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(...);
令牌
如果您想快速将令牌添加到请求的Authorization头部,可以使用withToken方法
$response = Http::withToken('token')->post(...);
超时
可以使用timeout方法指定等待响应的最大秒数
$response = Http::timeout(3)->get(...);
如果超出了给定的时间,将抛出一个Gokure\Http\Client\ConnectionException实例。
重试
如果您想使HTTP客户端在发生客户端或服务器错误时自动重试请求,可以使用retry方法。该retry方法接受两个参数:请求应该尝试的最大次数和Hyperf应在尝试之间等待的毫秒数
$response = Http::retry(3, 100)->post(...);
如果所有请求都失败,将抛出一个Gokure\Http\Client\RequestException实例。
错误处理
与Guzzle的默认行为不同,Hyperf的HTTP客户端包装器在客户端或服务器错误(来自服务器的400和500级响应)时不会抛出异常。您可以使用successful、clientError或serverError方法确定是否返回了这些错误之一
// Determine if the status code is >= 200 and < 300... $response->successful(); // Determine if the status code is >= 400... $response->failed(); // Determine if the response has a 400 level status code... $response->clientError(); // Determine if the response has a 500 level status code... $response->serverError();
抛出异常
如果您有一个响应实例,并且当响应状态码指示客户端或服务器错误时,想抛出一个Gokure\Http\Client\RequestException实例,可以使用throw方法
$response = Http::post(...); // Throw an exception if a client or server error occurred... $response->throw(); return $response['user']['id'];
Gokure\Http\Client\RequestException实例有一个公开的$response属性,这允许您检查返回的响应。
throw方法在没有错误发生的情况下返回响应实例,允许您将其他操作链接到throw方法
return Http::post(...)->throw()->json();
如果您想在抛出异常之前执行一些额外的逻辑,可以将闭包传递给throw方法。在调用闭包后,将自动抛出异常,因此您不需要在闭包内部重新抛出异常
return Http::post(...)->throw(function ($response, $e) { // })->json();
Guzzle选项
您可以使用withOptions方法指定额外的Guzzle请求选项。该方法接受一个键/值对的数组
$response = Http::withOptions([ 'debug' => true, ])->get('http://example.com/users');
并发请求
有时,您可能希望并发地发出多个HTTP请求。换句话说,您希望同时分发多个请求,而不是按顺序发出请求。这在与缓慢的HTTP API交互时可以带来显著的性能改进。
幸运的是,您可以使用pool方法完成这项操作。该方法接受一个闭包,该闭包接收一个Gokure\Http\Client\Pool实例,允许您轻松地将请求添加到请求池中以进行调度。
use Gokure\Http\Client\Pool; use Gokure\Http\Client\Http; $responses = Http::pool(fn (Pool $pool) => [ $pool->get('https:///first'), $pool->get('https:///second'), $pool->get('https:///third'), ]); return $responses[0]->ok() && $responses[1]->ok() && $responses[2]->ok();
如您所见,每个响应实例都可以根据其添加到池中的顺序进行访问。如果您愿意,可以使用as方法为请求命名,这样您就可以通过名称访问相应的响应。
use Gokure\Http\Client\Pool; use Gokure\Http\Client\Http; $responses = Http::pool(fn (Pool $pool) => [ $pool->as('first')->get('https:///first'), $pool->as('second')->get('https:///second'), $pool->as('third')->get('https:///third'), ]); return $responses['first']->ok();
测试
许多Hyperf服务提供功能,帮助您轻松且具有表达性地编写测试,Hyperf的HTTP包装器也不例外。HTTP外观的fake方法允许您指示HTTP客户端在请求时返回存根/模拟响应。
模拟响应
例如,要指示HTTP客户端为每个请求返回空或200状态码的响应,您可以不带参数调用fake方法。
use Gokure\Http\Client\Http; Http::fake(); $response = Http::post(...);
{note} 当模拟请求时,HTTP客户端中间件不会执行。您应该定义对模拟响应的预期,就像这些中间件已经正确运行一样。
模拟特定URL
或者,您可以将一个数组传递给fake方法。数组的键应表示您希望模拟的URL模式及其相关响应。可以使用*字符作为通配符。对未模拟的URL发出的任何请求实际上都会执行。您可以使用HTTP外观的response方法为这些端点构建存根/模拟响应。
Http::fake([ // Stub a JSON response for GitHub endpoints... 'github.com/*' => Http::response(['foo' => 'bar'], 200, $headers), // Stub a string response for Google endpoints... 'google.com/*' => Http::response('Hello World', 200, $headers), ]);
如果您希望指定一个后备URL模式,该模式将模拟所有不匹配的URL,您可以使用单个*字符。
Http::fake([ // Stub a JSON response for GitHub endpoints... 'github.com/*' => Http::response(['foo' => 'bar'], 200, ['Headers']), // Stub a string response for all other endpoints... '*' => Http::response('Hello World', 200, ['Headers']), ]);
模拟响应序列
有时您可能需要指定单个URL应按特定顺序返回一系列模拟响应。您可以使用Http::sequence方法构建响应以实现这一点。
Http::fake([ // Stub a series of responses for GitHub endpoints... 'github.com/*' => Http::sequence() ->push('Hello World', 200) ->push(['foo' => 'bar'], 200) ->pushStatus(404), ]);
当响应序列中的所有响应都已消费后,任何进一步的请求将导致响应序列抛出异常。如果您希望指定在序列为空时应返回的默认响应,您可以使用whenEmpty方法。
Http::fake([ // Stub a series of responses for GitHub endpoints... 'github.com/*' => Http::sequence() ->push('Hello World', 200) ->push(['foo' => 'bar'], 200) ->whenEmpty(Http::response()), ]);
如果您想要模拟一系列响应,但不需要指定应模拟的具体URL模式,您可以使用Http::fakeSequence方法。
Http::fakeSequence() ->push('Hello World', 200) ->whenEmpty(Http::response());
模拟回调
如果您需要更复杂的逻辑来确定对某些端点返回哪些响应,您可以将闭包传递给fake方法。此闭包将接收一个Gokure\Http\Client\Request实例,并应返回一个响应实例。在您的闭包中,您可以执行必要的任何逻辑来确定应返回哪种类型的响应。
Http::fake(function ($request) { return Http::response('Hello World', 200); });
检查请求
在模拟响应时,您有时可能希望检查客户端接收到的请求,以确保您的应用程序正在发送正确的数据或头信息。您可以通过在调用Http::fake后调用Http::assertSent方法来实现这一点。
assertSent 方法接受一个闭包,该闭包将接收一个 Gokure\Http\Client\Request 实例,并应返回一个布尔值,表示请求是否符合您的期望。为了使测试通过,至少必须发送一个符合给定期望的请求。
use Gokure\Http\Client\Request; use Gokure\Http\Client\Http; Http::fake(); Http::withHeaders([ 'X-First' => 'foo', ])->post('http://example.com/users', [ 'name' => 'Taylor', 'role' => 'Developer', ]); Http::assertSent(function (Request $request) { return $request->hasHeader('X-First', 'foo') && $request->url() == 'http://example.com/users' && $request['name'] == 'Taylor' && $request['role'] == 'Developer'; });
如果需要,您可以使用 assertNotSent 方法断言未发送特定的请求
use Gokure\Http\Client\Request; use Gokure\Http\Client\Http; Http::fake(); Http::post('http://example.com/users', [ 'name' => 'Taylor', 'role' => 'Developer', ]); Http::assertNotSent(function (Request $request) { return $request->url() === 'http://example.com/posts'; });
或者,您可以使用 assertNothingSent 方法来断言在测试期间未发送任何请求
Http::fake(); Http::assertNothingSent();
许可证
在 MIT 许可证下发布,请参阅 LICENSE。