HTTP 客户端

简介

Laravel 基于 Guzzle HTTP 客户端封装了一个优雅的、最小化的 API,从而方便开发者快速创建 HTTP 请求与其他 Web 应用进行通信。Laravel 对 Guzzle 的封装围绕的是最常用的场景,并且致力于提供更好的开发体验。

开始使用之前,需要确保已经在项目中安装过 Guzzle 扩展包依赖,默认情况下,Laravel 会自动包含这个依赖:

composer require guzzlehttp/guzzle

创建请求

基本使用

要创建请求,可以使用 getpostputpatch 以及 delete 方法。首先,让我们看看如何创建最基本的 GET 请求:

use Illuminate\Support\Facades\Http;

$response = Http::get('http://blog.test');

get 方法返回一个 Illuminate\Http\Client\Response 实例,该实例上提供了多个对响应进行「透视」的方法:

$response->body() : string;
$response->json() : array;
$response->status() : int;
$response->ok() : bool;
$response->successful() : bool;
$response->serverError() : bool;
$response->clientError() : bool;
$response->header($header) : string;
$response->headers() : array;

Illuminate\Http\Client\Response 对象还实现了 PHP ArrayAccess 接口,这样一来,就可以直接在响应实例上访问 JSON 响应数据:

return Http::get('http://blog.test/users/1')['name'];

请求数据

当然,在 POSTGETPATCH 请求中发送额外请求数据很常见,这些方法可以接收数组格式请求数据作为第二个参数。默认情况下,数据会以 application/json 内容类型发送:

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

发送表单 URL 编码请求

如果你想使用 application/x-www-form-urlencoded内容类型发送数据(一般 HTML 表单都是这个格式),需要在创建请求前调用 asForm 方法:

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

Multi-Part 请求

对于文件上传请求,需要以 multi-part 内容类型发起请求,这可以通过在创建请求前调用 attach 方法完成。该方法接收文件名和内容作为参数,还可以传递第三个参数作为期望文件名:

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

除了传递文件原生内容外,还可以传递流资源:

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

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

请求头

可以使用 withHeaders 方法添加请求头到请求,withHeaders 方法接收数据格式是键值对数组:

$response = Http::withHeaders([
    'X-First' => 'foo',
    'X-Second' => 'bar'
])->post('http://blog.test/users', [
    'name' => '学院君',
]);

认证

你可以使用 withBasicAuthwithDigestAuth 方法设置认证方式:

// Basic authentication...
$response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(...);

// Digest authentication...
$response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(...);

Bearer Tokens

如果你想要快速添加 Authorization Bearer Token 头到请求,可以使用 withToken 方法:

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

重试

如果你想要 HTTP 客户端在客户端或服务端发生错误时自动重发请求,可以使用 retry 方法。该方法接收两个参数 —— 重试次数和两次重试之间的时间间隔(ms):

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

如果所有重试也失败,则抛出 Illuminate\Http\Client\RequestException 异常。

错误处理

不同于 Guzzle 默认的行为,Laravel 的 HTTP 客户端封装并不会在客户端或服务端发生错误时抛出异常,你需要使用 successfulclientError 或者 serverError 方法来判断是否返回错误:

// Determine if the status code was >= 200 and < 300...
$response->successful();

// Determine if the response has a 400 level status code...
$response->clientError();

// Determine if the response has a 500 level status code...
$response->serverError();

抛出异常

如果你持有一个响应实例,并且在该响应是客户端或服务端错误的情况下想要抛出一个 Illuminate\Http\Client\RequestException 异常实例,可以使用 throw 方法:

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

// Throw an exception if a client or server error occurred...
$response->throw();

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

Illuminate\Http\Client\RequestException 实例包含一个公开的 $response 属性,你可以通过它来检查返回的响应。

throw 方法会在没有错误发生时返回响应实例,所以你可以通过方法链的形式在上面定义更多其他操作:

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

Guzzle 选项

你可以使用 withOptions 方法指定更多额外的 Guzzle 请求选项,该方法接收数组格式参数来指定多个选项:

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

测试

很多 Laravel 服务都提供了助力开发者轻松优雅编写测试代码的功能,Laravel HTTP 客户端封装库也不例外,通过 HTTP 门面的 fake 方法,你可以轻松构造 HTTP 客户端并在发起请求后返回预定义(通过桩文件或者模板代码设置)响应。

伪造响应

例如,要构建一个 HTTP 客户端针对任意请求都返回空实体、200 状态码响应,可以调用不传入任何参数的 fake 方法实现:

use Illuminate\Support\Facades\Http;

Http::fake();

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

伪造指定 URL

你还可以传递数组到 fake 方法,该数组的键表示你希望伪造的请求 URL 模式字符串,该数组的值表示与之关联的响应。在 URL 模式字符串中可以使用 * 作为通配符。在预定义响应中,我们使用的是 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 模式来处理所有未匹配请求,可以使用 *作为键:

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 方法。该回调函数接收一个 Illuminate\Http\Client\Request 实例并返回一个对应的响应实例:

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

检查请求

伪造响应时,你可能想要检查客户端请求以便确保应用发送的是正确的数据或者请求头。这可以通过在调用 Http::fake 之后调用 Http::assertSent 方法来实现。

assertSent 方法接收一个回调函数作为参数,该回调函数需要传入 Illuminate\Http\Client\Request 实例然后返回一个布尔类型的值,用来表明请求是否和预期匹配。为了让测试通过,至少要发出一个和预期匹配的请求:

Http::fake();

Http::withHeaders([
    'X-First' => 'foo',
])->post('http://blog.test/users', [
    'name' => '学院君',
    'role' => 'Developer',
]);

Http::assertSent(function ($request) {
    return $request->hasHeader('X-First', 'foo') &&
           $request->url() == 'http://blog.test/users' &&
           $request['name'] == '学院君' &&
           $request['role'] == 'Developer';
});

上一篇: 辅助函数

下一篇: 邮件