ZetCode

Symfony HttpClient

最后修改于 2020 年 7 月 5 日

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

有关更多信息,请阅读官方 The HttpComponent 文档。

Symfony

Symfony 是一套可重用的 PHP 组件,也是一个用于 Web 项目的 PHP 框架。Symfony 于 2005 年发布为自由软件。Symfony 的最初作者是 Fabien Potencier。Symfony 深受 Spring Framework 的启发。

在示例中,我们将使用 httpbin.orghttp://jsonplaceholder.typicode.com/ 在线服务。

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

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

HttpClient GET 请求

HTTP 定义了一系列请求方法,用于指示对给定资源要执行的期望操作。GET 请求用于从指定资源请求数据。使用 GET 的请求应该只检索数据,并且不应对数据产生其他影响。

注意: HTTP 方法的目的和影响是建议性的;它们不是严格的规则。这就是为什么我们之前说 GET 方法应该不对数据产生影响。在实践中,这并不总是被遵守。

get_request.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(),它们会等待直到接收到完整的响应内容。
$httpClient = HttpClient::create();
$response = $httpClient->request('GET', 'http://webcode.me');

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

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

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

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

从响应的头部,我们获取内容类型。

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

最后,我们使用 getContent() 方法获取页面的内容。

$ 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

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 请求和响应的在线工具。

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

在头部数组中,我们添加了 User-Agent 选项。

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

我们向 https://httpbin.org/user-agent URL 发送一个 GET 请求,该请求会返回请求的用户代理选项。

HttpClient toArray()

toArray() 方法将响应体解码为数组,通常来自 JSON 载荷。

content_array.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 的帖子。

dump($response->toArray());

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

$ 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 数据

POST 请求用于将数据发送到服务器。数据位于 HTTP 请求的请求体中。

POST 请求永远不会被缓存,它们不会保留在浏览器历史记录中,它们不能被收藏,并且对数据长度没有限制。

post_data.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 请求中将 message 变量发送到指定的 URL。在响应对象中,我们找到了在 POST 请求中发送的数据。

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

这是输出。

HttpClient 重定向

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

使用重定向的原因

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

redirect.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();

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

HttpClient 查询参数

查询参数是统一资源定位符 (URL) 的一部分,它为指定的参数分配值。这是将数据发送到目标服务器的一种方式。

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

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

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

query_params.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 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 容器用于测试。

$ docker run -p 80:80 kennethreitz/httpbin

我们运行容器。

docker_ex.php
<?php

require 'vendor/autoload.php';

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();

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

dump($response->toArray());

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

HTTP 基本身份验证

HTTP 基本身份验证是一种简单的挑战-响应机制,其中服务器向客户端请求凭据。客户端在 Authorization 头部中将凭据传递给服务器。身份验证信息不会以任何方式加密或哈希。它使用 Base64 算法进行编码。因此,HTTP 基本身份验证只有在使用 HTTPS 时才被认为是安全的。

HTTP 基本身份验证使用 HTTP 头部中的标准字段,无需握手。

authenticate.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 基本身份验证。

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

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

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

不要将 URL 中的用户和密码混淆;这仅仅是为了 httpbin 服务的测试。

HttpClient 流式传输数据

分块传输编码是一种流式数据传输机制,自 HTTP 1.1 起可用。在分块传输编码中,数据流被分成一系列非重叠的

块是独立发送和接收的。每个块前面都跟着它的字节大小。

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

streaming.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 镜像。

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

我们创建一个 GET 请求到指定的 URL;可选地,我们可以关闭内存缓冲。

$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 请求,该请求返回十个用户。

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

我们创建一个新的 Symfony skeleton 应用程序并安装一些依赖项。

config/packages/framework.yaml
framework:
...
  http_client:
      max_host_connections: 5
      default_options:
          max_redirects: 3

framework.yaml 文件中,我们可以配置 HttpClient。

$ php bin/console make:controller DataController

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

src/Controller/DataController.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

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

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

我们使用 HttpClientInterface 注入 HttpClient。

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 bin/console server:run

我们运行应用程序并导航到 localhost:8000/data

在本教程中,我们学习了 Symfony HttpClient 组件。

列出 所有 Symfony 教程