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。