# Symfony `HttpClient`教程

> 原文： [http://zetcode.com/symfony/httpclient/](http://zetcode.com/symfony/httpclient/)

Symfony `HttpClient`教程展示了如何使用`HttpClient`组件在 Symfony 中创建 HTTP 请求。 该组件提供使用 API​​ 的工具，并支持同步和异步操作。

有关更多信息，请阅读官方 [HttpComponent](https://symfony.com/doc/current/components/http_client.html) 文档。

## Symfony

Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 受到 Spring 框架的极大启发。

在示例中，我们将使用`httpbin.org`和`http://jsonplaceholder.typicode.com/`在线服务。

```php
$ composer require symfony/http-client
$ composer require symfony/var-dumper

```

我们安装了`HttpClient`和`var-dumper`组件。

## `HttpClient` GET 请求

HTTP 定义了一组请求方法，以指示要对给定资源执行的所需操作。 GET 请求用于从指定资源请求数据。 使用 GET 的请求应仅检索数据，而对数据没有其他影响。

> **注意**：建议使用 HTTP 方法的目的和影响； 这些不是严格的规则。 这就是我们前面说过 GET 方法不应对数据产生影响的原因。 实际上，这并不总是遵循的。

`get_request.php`

```php
<?php

require('vendor/autoload.php');

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();
$response = $httpClient->request('GET', 'http://webcode.me');

$statusCode = $response->getStatusCode();
echo $statusCode . "\n";

$contentType = $response->getHeaders()['content-type'][0];
echo $contentType . "\n";

$content = $response->getContent();
echo $content . "\n";

```

该示例创建`HttpClient`，并向指定的网页发出 GET 请求。

> **注意**：响应始终是异步的，因此对方法的调用将立即返回，而不是等待接收响应。 有诸如`getStatusCode()`和`getContent()`之类的阻塞方法，它们会等到接收到完整的响应内容为止。

```php
$httpClient = HttpClient::create();
$response = $httpClient->request('GET', 'http://webcode.me');

```

我们用`HttpClient::create()`创建一个`HttpClient`。 使用`request()`方法生成 GET 请求。

```php
$statusCode = $response->getStatusCode();
echo $statusCode . "\n";

```

我们使用`getStatusCode()`方法获取状态代码。

```php
$contentType = $response->getHeaders()['content-type'][0];
echo $contentType . "\n";

```

从响应的标题中，我们获得内容类型。

```php
$content = $response->getContent();
echo $content . "\n";

```

最后，我们使用`getContent()`方法获得页面的内容。

```php
$ php get_request.php
200
text/html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My html page</title>
</head>
<body>

    <p>
        Today is a beautiful day. We go swimming and fishing.
    </p>

    <p>
          Hello there. How are you?
    </p>

</body>
</html>

```

这是输出。

## `HttpClient`用户代理

创建`HttpClient`时，我们可以传递一些选项，例如标头值。

`user_agent.php`

```php
<?php

require('vendor/autoload.php');

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create(['headers' => [
    'User-Agent' => 'PHP console app',
]]);

$response = $httpClient->request('GET', 'https://httpbin.org/user-agent');

echo $response->getContent() . "\n";    

```

我们连接到`httpbin.org`网站，该网站是用于测试 HTTP 请求&响应的在线工具。

```php
$httpClient = HttpClient::create(['headers' => [
    'User-Agent' => 'PHP console app',
]]);

```

在标题数组中，我们添加`User-Agent`选项。

```php
$response = $httpClient->request('GET', 'https://httpbin.org/user-agent');

```

我们将 GET 请求发送到`https://httpbin.org/user-agent` URL，该 URL 返回请求的用户代理选项。

## `HttpClient toArray()`

`toArray()`方法通常将响应主体解码为数组，并从 JSON 有效负载解码为数组。

`content_array.php`

```php
<?php

require('vendor/autoload.php');

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();

$response = $httpClient->request('GET', 
        'https://jsonplaceholder.typicode.com/posts/2/');

dump($response->toArray());

```

该示例向`jsonplaceholder.typicode.com`在线服务网站发出 GET 请求，该网站以 JSON 格式返回 ID 为 2 的帖子。

```php
dump($response->toArray());

```

我们转储`toArray()`方法的输出。

```php
$ php content_array.php
array:4 [
  "userId" => 1
  "id" => 2
  "title" => "qui est esse"
  "body" => """
    est rerum tempore vitae\n
    sequi sint nihil reprehenderit dolor beatae ea dolores neque\n
    fugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\n
    qui aperiam non debitis possimus qui neque nisi nulla
    """
]

```

这是输出。

## `HttpClient` POST 数据

发布请求用于将数据发送到服务器。 数据位于 HTTP 请求的请求正文中。

发布请求永远不会被缓存，它们不会保留在浏览器历史记录中，也无法被添加书签，并且数据长度没有限制。

`post_data.php`

```php
<?php

require('vendor/autoload.php');

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();

$response = $httpClient->request('POST', 'https://httpbin.org/post', [
    'body' => ['msg' => 'Hello there']
]);

echo $response->getContent();

```

在示例中，我们在 POST 请求中将消息变量发送到指定的 URL。 在响应对象中，我们找到了 POST 请求中发送的数据。

```php
$ php post_data.php
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "msg": "Hello there"
  },
  ...
}

```

这是输出。

## `HttpClient`重定向

URL 重定向是将请求从一页转发到另一页的过程。 当 Web 浏览器尝试打开已重定向的 URL 时，将打开一个具有其他 URL 的页面。 一个 URL 可能有多个重定向。

使用重定向的原因：

*   URL 缩短
*   防止在移动网页时断开链接
*   允许属于同一所有者的多个域名引用一个网站
*   隐私保护
*   HTTP 到 HTTP 的过渡
*   恶意目的，例如网络钓鱼攻击或恶意软件分发

发出请求时，`HttpClient`遵循重定向，最多 20 个。 `max_redirects`属性用于配置此行为。 值 0 表示不遵循任何重定向。

`redirect.php`

```php
<?php

require 'vendor/autoload.php';

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();

$response = $httpClient->request('GET', 'https://httpbin.org/redirect/4', [
    'max_redirects' => 3,
]);

echo $response->getStatusCode();

```

我们将 GET 请求发送到重定向四次的 URL，同时将`max_redirects`属性设置为三。 这意味着我们获得 302 重定向状态代码。 如果我们增加`max_redirects`值，我们应该得到 200。

## `HttpClient`查询参数

查询参数是统一资源定位器（URL）的一部分，该 URL 将值分配给指定的参数。 这是将数据发送到目标服务器的一种方法。

```php
http://example.com/api/users?name=John%20Doe&occupation=gardener

```

查询参数在`?`字符之后指定。`&`分隔了多个字段。 特殊字符（例如空格）被编码。 在上面的字符串中，空格用`%20`值编码。

Symfony `HttpClient`在将值包含在 URL 中之前会自动对其进行编码。

`query_params.php`

```php
<?php

require('vendor/autoload.php');

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();

$response = $httpClient->request('GET', 'https://httpbin.org/get', [

    'query' => [
        'name' => 'John Doe',
    ],
]);

echo $response->getContent();

```

我们将`name`字段发送到`https://httpbin.org/get` URL。 在响应中，我们返回了 URL 参数。

```php
$ php query_params.php
{
  "args": {
    "name": "John Doe"
  },
  "headers": {
    "Accept-Encoding": "deflate, gzip",
    "Host": "httpbin.org",
    "User-Agent": "Symfony HttpClient/Curl"
  },
  ...
}

```

这是输出。

## 使用 httpbin 的 Docker 容器

`httpbin.org`还提供了一个用于测试的 Docker 容器。

```php
$ docker run -p 80:80 kennethreitz/httpbin

```

我们运行容器。

`docker_ex.php`

```php
<?php

require 'vendor/autoload.php';

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();

$response = $httpClient->request('GET', 'http://localhost:80/anything',
    [
        'json' => ['message' => 'Hello there'],
    ]);

dump($response->toArray());

```

在示例中，我们使用 httpbin 的服务连接到容器。 `localhost:80/anything`返回传递给请求的任何内容。

## HTTP 基本认证

HTTP 基本认证是一种简单的质询和响应机制，其中服务器从客户端请求凭据。 客户端在授权标头中将凭据传递给服务器。 认证信息不以任何方式加密或哈希。 它使用 Base64 算法编码。 因此，仅将 HTTP 基本认证与`HTTPS`一起使用才被认为是安全的。

HTTP 基本认证使用 HTTP 标头中的标准字段，从而无需握手。

`authenticate.php`

```php
<?php

require('vendor/autoload.php');

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create([
    'auth_basic' => ['user7', 'passwd']
]);

$response = $httpClient->request('GET', 
    'https://httpbin.org/basic-auth/user7/passwd');

echo $response->getStatusCode();

dump($response);

```

在示例中，我们使用 HTTP 基本认证。

```php
$httpClient = HttpClient::create([
    'auth_basic' => ['user7', 'passwd']
]);

```

HTTP 基本认证通过`auth_basic`选项指定。 所有请求将使用相同的凭据。

```php
$response = $httpClient->request('GET', 
  'https://httpbin.org/basic-auth/user7/passwd');

```

不要与 URL 中的用户名和密码混淆； 这仅用于在 httpbin 的服务中进行测试。

## `HttpClient`流数据

自 HTTP 1.1 以来，块传输编码是一种流数据传输机制。 在分块传输编码中，数据流被分为一系列不重叠的块。

块彼此独立地发送和接收。 每个块之前都有其大小（以字节为单位）。

在 Symfony `HttpClient`中，流传输是通过`stream()`完成的。

`streaming.php`

```php
<?php

require 'vendor/autoload.php';

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();

$url = 'https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES/12.0/FreeBSD-12.0-RELEASE-amd64-mini-memstick.img';
$response = $httpClient->request('GET', $url, [
    'buffer' => false,
]);

if (200 !== $response->getStatusCode()) {
    throw new \Exception('Failed to create a request');
}

$fileHandler = fopen('freebsd-12.0-amd64-mini-memstick.iso', 'w');
foreach ($httpClient->stream($response) as $chunk) {
    fwrite($fileHandler, $chunk->getContent());
}

```

在示例中，我们下载了 FreeBSD ISO 映像。

```php
$response = $httpClient->request('GET', $url, [
  'buffer' => false,
]);

```

我们创建一个对指定 URL 的 GET 请求； （可选）我们可以关闭内存缓冲。

```php
$fileHandler = fopen('freebsd-12.0-amd64-mini-memstick.iso', 'w');
foreach ($httpClient->stream($response) as $chunk) {
    fwrite($fileHandler, $chunk->getContent());
}

```

我们以块的形式获取响应内容，并将其保存在文件中。

## Symfony `HttClient` Webapp 示例

在下面的示例中，我们创建一个 Symfony Web 应用，该应用使用`HttpClient`生成请求。 我们使用`HttpClientInterface`注入`HttpClient`。

该应用向`https://jsonplaceholder.typicode.com/users`发出 GET 请求，该请求返回十个用户。

```php
$ composer create-project symfony/skeleton symfapp
$ cd symfapp
$ composer require annotations
$ composer require maker server --dev
$ composer require symfony/http-client

```

我们创建一个新的 Symfony 框架应用并安装一些依赖项。

`config/packages/framework.yaml`

```php
framework:
...
  http_client:
      max_host_connections: 5
      default_options:
          max_redirects: 3

```

在`framework.yaml`文件中，我们可以配置`HttpClient`。

```php
$ php bin/console make:controller DataController

```

我们创建一个新的控制器。

`src/Controller/DataController.php`

```php
<?php

namespace App\Controller;

use App\Service\UserService;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class DataController extends AbstractController
{
    /**
     * @Route("/data", name="data")
     */
    public function index(UserService $userService): Response
    {
        $data = $userService->getUsers();

        return $this->json($data);
    }
}

```

`DataController`注入`UserService`，调用其`getUsers()`方法以检索数据。 数据作为 JSON 返回给调用方。

`src/Service/UserService.php`

```php
<?php

namespace App\Service;

use Symfony\Contracts\HttpClient\HttpClientInterface;

class UserService
{
    private $httpClient;

    public function __construct(HttpClientInterface $httpClient)
    {
        $this->httpClient = $httpClient;
    }

    public function getUsers(): Array
    {
        $response = $this->httpClient->request('GET', 
            'https://jsonplaceholder.typicode.com/users');

        $data = $response->getContent();

        $decoded = json_decode($data);

        return $decoded;
    }
}

```

这是`UserService.`

```php
public function __construct(HttpClientInterface $httpClient)
{
    $this->httpClient = $httpClient;
}

```

我们用`HttpClientInterface`注入 HttpClient。

```php
public function getUsers(): Array
{
    $response = $this->httpClient->request('GET', 
        'https://jsonplaceholder.typicode.com/users');

    $data = $response->getContent();

    $decoded = json_decode($data);

    return $decoded;
}

```

我们生成一个 GET 请求，解码数据并返回它。

```php
$ php bin/console server:run

```

我们运行该应用并导航到`localhost:8000/data`。

在本教程中，我们使用了 Symfony `HttpClient`组件。

您可能也对以下相关教程感兴趣： [Symfony 简介](/symfony/intro/)， [Symfony 验证教程](/symfony/validation/)， [Symfony 服务教程](/symfony/service/)， [Symfony 表单教程](/symfony/form/) ， [PHP 教程](/lang/php/)或列出[所有 Symfony 教程](/all/#symfony)。