Skip to content
赞助商赞助商赞助商
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待

HTTP 客户端

介绍

Laravel 提供了一个围绕 Guzzle HTTP 客户端的简洁、最小化的 API,使您能够快速发起外部 HTTP 请求以与其他 Web 应用程序通信。Laravel 对 Guzzle 的封装专注于其最常见的用例和出色的开发者体验。

在开始之前,您应该确保已将 Guzzle 包安装为应用程序的依赖项。默认情况下,Laravel 会自动包含此依赖项。但是,如果您之前删除了该包,可以通过 Composer 重新安装:

php
composer require guzzlehttp/guzzle

发起请求

要发起请求,您可以使用 Http facade 提供的 headgetpostputpatchdelete 方法。首先,让我们看看如何向另一个 URL 发起基本的 GET 请求:

php
use Illuminate\Support\Facades\Http;

$response = Http::get('http://example.com');

get 方法返回一个 Illuminate\Http\Client\Response 实例,该实例提供了多种方法可用于检查响应:

php
$response->body() : string;
$response->json($key = null) : array|mixed;
$response->object() : object;
$response->collect($key = null) : Illuminate\Support\Collection;
$response->status() : int;
$response->ok() : bool;
$response->successful() : bool;
$response->redirect(): bool;
$response->failed() : bool;
$response->serverError() : bool;
$response->clientError() : bool;
$response->header($header) : string;
$response->headers() : array;

Illuminate\Http\Client\Response 对象还实现了 PHP 的 ArrayAccess 接口,允许您直接在响应上访问 JSON 响应数据:

php
return Http::get('http://example.com/users/1')['name'];

转储请求

如果您希望在发送请求实例之前转储它并终止脚本的执行,可以在请求定义的开头添加 dd 方法:

php
return Http::dd()->get('http://example.com');

请求数据

当然,在发起 POSTPUTPATCH 请求时,通常会发送附加数据,因此这些方法接受一个数组作为第二个参数。默认情况下,数据将使用 application/json 内容类型发送:

php
use Illuminate\Support\Facades\Http;

$response = Http::post('http://example.com/users', [
    'name' => 'Steve',
    'role' => 'Network Administrator',
]);

GET 请求查询参数

在发起 GET 请求时,您可以直接将查询字符串附加到 URL,或者将键/值对数组作为第二个参数传递给 get 方法:

php
$response = Http::get('http://example.com/users', [
    'name' => 'Taylor',
    'page' => 1,
]);

发送表单 URL 编码请求

如果您希望使用 application/x-www-form-urlencoded 内容类型发送数据,应在发起请求之前调用 asForm 方法:

php
$response = Http::asForm()->post('http://example.com/users', [
    'name' => 'Sara',
    'role' => 'Privacy Consultant',
]);

发送原始请求体

如果您希望在发起请求时提供原始请求体,可以使用 withBody 方法。内容类型可以通过方法的第二个参数提供:

php
$response = Http::withBody(
    base64_encode($photo), 'image/jpeg'
)->post('http://example.com/photo');

多部分请求

如果您希望以多部分请求的形式发送文件,应在发起请求之前调用 attach 方法。此方法接受文件的名称及其内容。如果需要,您可以提供第三个参数作为文件的文件名:

php
$response = Http::attach(
    'attachment', file_get_contents('photo.jpg'), 'photo.jpg'
)->post('http://example.com/attachments');

而不是传递文件的原始内容,您可以传递一个流资源:

php
$photo = fopen('photo.jpg', 'r');

$response = Http::attach(
    'attachment', $photo, 'photo.jpg'
)->post('http://example.com/attachments');

请求头

可以使用 withHeaders 方法向请求添加请求头。此 withHeaders 方法接受一个键/值对数组:

php
$response = Http::withHeaders([
    'X-First' => 'foo',
    'X-Second' => 'bar'
])->post('http://example.com/users', [
    'name' => 'Taylor',
]);

您可以使用 accept 方法指定您的应用程序期望在响应中接收的内容类型:

php
$response = Http::accept('application/json')->get('http://example.com/users');

为了方便起见,您可以使用 acceptJson 方法快速指定您的应用程序期望在响应中接收 application/json 内容类型:

php
$response = Http::acceptJson()->get('http://example.com/users');

认证

您可以使用 withBasicAuthwithDigestAuth 方法分别指定基本和摘要认证凭据:

php
// 基本认证...
$response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(...);

// 摘要认证...
$response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(...);

Bearer 令牌

如果您希望快速将 bearer 令牌添加到请求的 Authorization 请求头中,可以使用 withToken 方法:

php
$response = Http::withToken('token')->post(...);

超时

可以使用 timeout 方法指定等待响应的最大秒数:

php
$response = Http::timeout(3)->get(...);

如果超出了给定的超时时间,将抛出 Illuminate\Http\Client\ConnectionException 实例。

重试

如果您希望 HTTP 客户端在发生客户端或服务器错误时自动重试请求,可以使用 retry 方法。retry 方法接受请求应尝试的最大次数以及 Laravel 在尝试之间应等待的毫秒数:

php
$response = Http::retry(3, 100)->post(...);

如果需要,您可以将第三个参数传递给 retry 方法。第三个参数应为一个可调用函数,用于确定是否应实际尝试重试。例如,您可能希望仅在初始请求遇到 ConnectionException 时重试请求:

php
$response = Http::retry(3, 100, function ($exception) {
    return $exception instanceof ConnectionException;
})->post(...);

如果所有请求都失败,将抛出 Illuminate\Http\Client\RequestException 实例。

错误处理

与 Guzzle 的默认行为不同,Laravel 的 HTTP 客户端封装不会在客户端或服务器错误(来自服务器的 400500 级别响应)时抛出异常。您可以使用 successfulclientErrorserverError 方法确定是否返回了这些错误之一:

php
// 确定状态码是否 >= 200 且 < 300...
$response->successful();

// 确定状态码是否 >= 400...
$response->failed();

// 确定响应是否具有 400 级别状态码...
$response->clientError();

// 确定响应是否具有 500 级别状态码...
$response->serverError();

// 如果存在客户端或服务器错误,立即执行给定的回调...
$response->onError(callable $callback);

抛出异常

如果您有一个响应实例,并希望在响应状态码指示客户端或服务器错误时抛出 Illuminate\Http\Client\RequestException 实例,可以使用 throwthrowIf 方法:

php
$response = Http::post(...);

// 如果发生客户端或服务器错误,则抛出异常...
$response->throw();

// 如果发生错误且给定条件为真,则抛出异常...
$response->throwIf($condition);

return $response['user']['id'];

Illuminate\Http\Client\RequestException 实例具有一个公共的 $response 属性,允许您检查返回的响应。

如果没有发生错误,throw 方法将返回响应实例,允许您在 throw 方法上链接其他操作:

php
return Http::post(...)->throw()->json();

如果您希望在抛出异常之前执行一些额外的逻辑,可以将闭包传递给 throw 方法。异常将在闭包调用后自动抛出,因此您不需要在闭包中重新抛出异常:

php
return Http::post(...)->throw(function ($response, $e) {
    //
})->json();

Guzzle 选项

您可以使用 withOptions 方法指定其他 Guzzle 请求选项withOptions 方法接受一个键/值对数组:

php
$response = Http::withOptions([
    'debug' => true,
])->get('http://example.com/users');

并发请求

有时,您可能希望同时发起多个 HTTP 请求。换句话说,您希望同时发送多个请求,而不是顺序发出请求。当与缓慢的 HTTP API 交互时,这可以显著提高性能。

幸运的是,您可以使用 pool 方法实现这一点。pool 方法接受一个闭包,该闭包接收一个 Illuminate\Http\Client\Pool 实例,允许您轻松地将请求添加到请求池中以进行调度:

php
use Illuminate\Http\Client\Pool;
use Illuminate\Support\Facades\Http;

$responses = Http::pool(fn (Pool $pool) => [
    $pool->get('http://localhost/first'),
    $pool->get('http://localhost/second'),
    $pool->get('http://localhost/third'),
]);

return $responses[0]->ok() &&
       $responses[1]->ok() &&
       $responses[2]->ok();

如您所见,可以根据请求添加到池中的顺序访问每个响应实例。如果您愿意,可以使用 as 方法命名请求,这样可以通过名称访问相应的响应:

php
use Illuminate\Http\Client\Pool;
use Illuminate\Support\Facades\Http;

$responses = Http::pool(fn (Pool $pool) => [
    $pool->as('first')->get('http://localhost/first'),
    $pool->as('second')->get('http://localhost/second'),
    $pool->as('third')->get('http://localhost/third'),
]);

return $responses['first']->ok();

Laravel HTTP 客户端允许您定义“宏”,这可以作为一种流畅、富有表现力的机制,用于在整个应用程序中与服务交互时配置常见的请求路径和请求头。要开始,您可以在应用程序的 App\Providers\AppServiceProvider 类的 boot 方法中定义宏:

php
use Illuminate\Support\Facades\Http;

/**
 * 启动任何应用程序服务。
 *
 * @return void
 */
public function boot()
{
    Http::macro('github', function () {
        return Http::withHeaders([
            'X-Example' => 'example',
        ])->baseUrl('https://github.com');
    });
}

一旦您的宏配置完成,您可以在应用程序的任何地方调用它,以创建具有指定配置的待处理请求:

php
$response = Http::github()->get('/');

测试

许多 Laravel 服务提供功能,帮助您轻松而富有表现力地编写测试,Laravel 的 HTTP 封装也不例外。Http facade 的 fake 方法允许您指示 HTTP 客户端在发起请求时返回模拟/虚拟响应。

伪造响应

例如,要指示 HTTP 客户端为每个请求返回空的、200 状态码的响应,您可以在不传递参数的情况下调用 fake 方法:

php
use Illuminate\Support\Facades\Http;

Http::fake();

$response = Http::post(...);

NOTE

在伪造请求时,HTTP 客户端中间件不会被执行。您应该为伪造的响应定义期望,就好像这些中间件已正确运行一样。

伪造特定 URL

或者,您可以将数组传递给 fake 方法。数组的键应表示您希望伪造的 URL 模式及其关联的响应。* 字符可以用作通配符。对未伪造的 URL 发起的任何请求将实际执行。您可以使用 Http facade 的 response 方法为这些端点构建模拟/虚拟响应:

php
Http::fake([
    // 为 GitHub 端点模拟 JSON 响应...
    'github.com/*' => Http::response(['foo' => 'bar'], 200, $headers),

    // 为 Google 端点模拟字符串响应...
    'google.com/*' => Http::response('Hello World', 200, $headers),
]);

如果您希望指定一个回退 URL 模式,以便为所有未匹配的 URL 模拟响应,可以使用单个 * 字符:

php
Http::fake([
    // 为 GitHub 端点模拟 JSON 响应...
    'github.com/*' => Http::response(['foo' => 'bar'], 200, ['Headers']),

    // 为所有其他端点模拟字符串响应...
    '*' => Http::response('Hello World', 200, ['Headers']),
]);

伪造响应序列

有时,您可能需要指定单个 URL 应按特定顺序返回一系列虚拟响应。您可以使用 Http::sequence 方法构建响应来实现这一点:

php
Http::fake([
    // 为 GitHub 端点模拟一系列响应...
    'github.com/*' => Http::sequence()
                            ->push('Hello World', 200)
                            ->push(['foo' => 'bar'], 200)
                            ->pushStatus(404),
]);

当响应序列中的所有响应都已被消耗时,任何进一步的请求将导致响应序列抛出异常。如果您希望指定一个默认响应,当序列为空时应返回该响应,可以使用 whenEmpty 方法:

php
Http::fake([
    // 为 GitHub 端点模拟一系列响应...
    'github.com/*' => Http::sequence()
                            ->push('Hello World', 200)
                            ->push(['foo' => 'bar'], 200)
                            ->whenEmpty(Http::response()),
]);

如果您希望伪造一系列响应,但不需要指定应伪造的特定 URL 模式,可以使用 Http::fakeSequence 方法:

php
Http::fakeSequence()
        ->push('Hello World', 200)
        ->whenEmpty(Http::response());

伪造回调

如果您需要更复杂的逻辑来确定某些端点应返回的响应,可以将闭包传递给 fake 方法。此闭包将接收一个 Illuminate\Http\Client\Request 实例,并应返回一个响应实例。在您的闭包中,您可以执行任何必要的逻辑以确定返回何种类型的响应:

php
Http::fake(function ($request) {
    return Http::response('Hello World', 200);
});

检查请求

在伪造响应时,您可能偶尔希望检查客户端接收到的请求,以确保您的应用程序发送了正确的数据或请求头。您可以通过在调用 Http::fake 之后调用 Http::assertSent 方法来实现这一点。

assertSent 方法接受一个闭包,该闭包将接收一个 Illuminate\Http\Client\Request 实例,并应返回一个布尔值,指示请求是否符合您的期望。为了使测试通过,至少必须发出一个符合给定期望的请求:

php
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\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 方法断言未发送特定请求:

php
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\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';
});

您可以使用 assertSentCount 方法断言在测试期间“发送”了多少请求:

php
Http::fake();

Http::assertSentCount(5);

或者,您可以使用 assertNothingSent 方法断言在测试期间未发送任何请求:

php
Http::fake();

Http::assertNothingSent();

事件

Laravel 在发送 HTTP 请求的过程中会触发三个事件。RequestSending 事件在请求发送之前触发,而 ResponseReceived 事件在接收到给定请求的响应后触发。如果未接收到给定请求的响应,则触发 ConnectionFailed 事件。

RequestSendingConnectionFailed 事件都包含一个公共的 $request 属性,您可以使用它来检查 Illuminate\Http\Client\Request 实例。同样,ResponseReceived 事件包含一个 $request 属性以及一个 $response 属性,可用于检查 Illuminate\Http\Client\Response 实例。您可以在 App\Providers\EventServiceProvider 服务提供者中为此事件注册事件监听器:

php
/**
 * 应用程序的事件监听器映射。
 *
 * @var array
 */
protected $listen = [
    'Illuminate\Http\Client\Events\RequestSending' => [
        'App\Listeners\LogRequestSending',
    ],
    'Illuminate\Http\Client\Events\ResponseReceived' => [
        'App\Listeners\LogResponseReceived',
    ],
    'Illuminate\Http\Client\Events\ConnectionFailed' => [
        'App\Listeners\LogConnectionFailed',
    ],
];