Русский
Русский
English
Статистика
Реклама

Psr-18

HTTP-клиент на стероидах

14.09.2020 14:13:59 | Автор: admin

Бывает так, что в проект не хочется тащить мощный и тяжелый HTTP-клиент, а хочется взять что-то легкое, но его функционала не достаточно. Для решения таких компромиссов у меня есть небольшие классы декораторы, которые я опубликовал на всеобщее обозрение под лицензией MIT.

Смена версии протокола

К нам пришёл HTTP/2, но не каждый сервер, как и не каждый клиент его поддерживает. Если попробовать отправить запрос, принудительно указав версию протокола 2, можно получить от сервера ошибку 505 HTTP Version Not Supported. Пакет webclient/ext-protocol-version решает эту, возможно надуманную, проблему. При получении ответа 505 клиент повторит запрос, но уже с версией протокола, указанной в ответе сервера.

<?phpuse Webclient\Extension\ProtocolVersion\Client;use Psr\Http\Client\ClientInterface;use Psr\Http\Message\RequestInterface;/**  * @var ClientInterface $client Ваш PSR-18 совместимый HTTP Client. */$http = new Client($client);/** @var RequestInterface $request */$response = $http->sendRequest($request);

Редиректы

Очень редко, но встречается, когда клиент не умеет следовать редиректам при ответе с кодом 3xx. В этом случае поможет пакет webclient/ext-redirect. Тут всё элементарно, Передаём в конструктор наш клиент и максимальное количество допустимых редиректов на один запрос.

<?phpuse Webclient\Extension\Redirect\Client;use Psr\Http\Client\ClientInterface;use Psr\Http\Message\RequestInterface;/**  * @var ClientInterface $client Ваш PSR-18 совместимый HTTP Client. * @var int $maxRedirects Максимальное количество допустимых редиректов. */$http = new Client($client, $maxRedirects);/** @var RequestInterface $request */$response = $http->sendRequest($request);

Логирование

Иногда нам необходимо логировать запросы и ответы. Пакет webclient/ext-log позволяет настроить логирование так, как этого требует проект. Помимо клиента, вам понадобится PSR-3 совместимый логгер. Для формирования строки лога используется интерфейс Webclient\Extension\Log\Formatter\Formatter, а для формирования ID запроса (для поиска в логах пары запрос-ответ) - Webclient\Extension\Log\IdGenerator\IdGenerator. По одной реализации этих интерфейсов поставляется из коробки:

  • Webclient\Extension\Log\IdGenerator\UniqueIdGenerator - элементарный генератор на основе uniqid()

  • Webclient\Extension\Log\Formatter\RawHttpFormatter - логирование запросов и ответов в виде RAW-текста.

<?phpuse Psr\Http\Client\ClientInterface;use Psr\Http\Message\RequestInterface;use Psr\Log\LoggerInterface;use Psr\Log\LogLevel;use Webclient\Extension\Log\Client;use Webclient\Extension\Log\Formatter\Formatter;use Webclient\Extension\Log\IdGenerator\IdGenerator;/**  * @var ClientInterface $client Ваш PSR-18 совместимый HTTP Client. * @var LoggerInterface $logger Ваш PSR-3 совместимый логгер. * @var IdGenerator|null $idGenerator Ваш ID-генератор. *      При передаче null будет использоваться  *      Webclient\Extension\Log\IdGenerator\UniqueIdGenerator. * @var Formatter|null $formatter Ваш форматировщик лога.  *      При передаче null будет использоваться  *      Webclient\Extension\Log\Formatter\RawHttpFormatter. */$http = new Client(    $client,    $logger,    $idGenerator,    $formatter,    LogLevel::INFO, // Уровень логирования запросов    LogLevel::INFO, // Уровень логирования информационных ответов (Коды 1xx)    LogLevel::INFO, // Уровень логирования успешных ответов (Коды 2xx)    LogLevel::INFO, // Уровень лоигрования ответов с редиректом (Коды 3xx)    LogLevel::ERROR, // Уровень логирования ответов об ошибках клиента (Коды 4xx)    LogLevel::ERROR, // Уровень логирования ответов об ошибках сервера (Коды 5xx)    LogLevel::WARNING // Уровень логирования исключений HTTP клиента);/** @var RequestInterface $request */$response = $http->sendRequest($request);

Куки

Бывают проекты, в которых не достаточно просто отправить запрос и получить ответ. Иногда нужно поддерживать сессию (ну или ещё что-то). Чтобы добавить вашему клиенту поддержку печенек, нужно отнаследоваться от абстрактного класса Webclient\Extension\Cookie\Cookie\Storage из пакета webclient/ext-cookie, либо воспользоваться одной из поставляемых с пакетом реализацией:

  • Webclient\Extension\Cookie\Cookie\ArrayStorage - держит куки в памяти. Слетает после завершения скрипта;

  • Webclient\Extension\Cookie\Cookie\NetscapeCookieFile - хранит куки в файле в соответствии с форматом Netscape.

<?phpuse Psr\Http\Client\ClientInterface;use Psr\Http\Message\RequestInterface;use Webclient\Extension\Cookie\Client;use Webclient\Extension\Cookie\Cookie\Storage;/**  * @var ClientInterface $client Ваш PSR-18 совместимый HTTP Client. * @var Storage $storage Хранилище куков. *      Вы можете отнаследоваться от этого класса для реализации своего хранилища. */$http = new Client($client, $storage);/** @var RequestInterface $request */$response = $http->sendRequest($request);

Кэширование

О кэшировании, его плюсах и минусах сказано уже много. Если вам хочется разгрузить какой-то из своих редко обновляемых микросервисов (и нагрузить кэш), поможет пакет webclient/ext-cache. Чтобы завернуть в него свой клиент, вам понадобится реализация Psr\SimpleCache\CacheInterface из PSR-6, Psr\Http\Message\ResponseFactoryInterface и Psr\Http\Message\StreamFactoryInterface из PSR-18.

Кэширование происходит на основе соответствующих заголовков HTTP-запросов и ответов.

<?phpuse Psr\Http\Client\ClientInterface;use Psr\Http\Message\RequestInterface;use Psr\Http\Message\ResponseFactoryInterface;use Psr\Http\Message\StreamFactoryInterface;use Psr\SimpleCache\CacheInterface;use Webclient\Extension\Cache\Client;/**  * @var ClientInterface $client Ваш PSR-18 совместимый HTTP Client. * @var CacheInterface $cache Ваш PSR-6 совместимый кэш. * @var ResponseFactoryInterface $responseFactory  *      Ваша PSR-17 совместимая фабрика ответов. * @var StreamFactoryInterface $streamFactory *      Ваша PSR-17 совместимая фабрика потоков. * @var string Строка, уникальная для приватного кэша (например, ID сессии). */$http = new Client(  $client,  $cache,  $responseFactory,  $streamFactory,  $privateKey);/** @var RequestInterface $request */$response = $http->sendRequest($request);

На этом расширения для клиентов заканчиваются, но статья продолжается. У меня есть ещё парочка инструментов, которые могут кому-то облегчить жизнь.

Создание запросов с файлами

Как известно, в PSR-7 есть два вида запросов - обычный запрос и серверный запрос. Они оба реализуют интерфейс Psr\Http\Message\RequestInterface (Psr\Http\Message\ServerRequestInterface его расширяет). При отправке файлов на сервер мы не можем просто взять серверный запрос, выполнить его метод withUploadedFiles($files) и передать полученный объект в HTTP-клиент. Для того, чтобы клиент корректно отправил запрос с файлами, эти файлы должны быть записаны в поток тела запроса. Библиотека webclient/helper-form предназначена для упрощения создания таких запросов. Для работы вам понадобятся реализации интерфейсов Psr\Http\Message\ResponseFactoryInterface и Psr\Http\Message\StreamFactoryInterface.

фейковый клиент

В проектах бывает необходимость запросов к сервисам, которые не желательно дергать лишний раз (например, платные API), но код, использующий такие обращения, тестировать нужно.

Для такого тестирования можно воспользоваться webclient/fake-http-client, который является реализацией Psr\Http\Client\ClientInterface, но под капотом вместо запроса к серверу вызывает Psr\Http\Server\RequestHandlerInterface из PSR-15 (преобразовав при необходимости Psr\Http\Message\RequestInterface в Psr\Http\Message\ServerRequestInterface). Реализация Psr\Http\Server\RequestHandlerInterface остаётся за вами - эмулируйте поведение, как вам нужно для тестирования.

<?phpuse Webclient\Fake\Client;use Psr\Http\Message\RequestInterface;use Psr\Http\Server\RequestHandlerInterface;/**  * @var RequestHandlerInterface $handler Ваш обработчик запроса.  * @var array $serverParams Параметры сервера, которые будут добавлены  *      при преобразовании из Psr\Http\Message\RequestInterface в *      Psr\Http\Message\ServerRequestInterface. */$client = new Client($handler, $serverParams);/** * @var RequestInterface $request Ваш HTTP-запрос */$response = $client->sendRequest($request);

Если вы передаете объект Psr\Http\Message\ServerRequestInterface клиенту и хотите, чтобы обработчик получил его как есть, добавьте атрибут Webclient\Fake\Client::NOREPLACEATTRIBUTE (иначе будет создан новый объект запроса).

<?phpuse Webclient\Fake\Client;use Psr\Http\Server\RequestHandlerInterface;/**  * @var Client $client.  * @var ServerRequestInterface $request. */$request = $request->withAttribute(Client::NOREPLACEATTRIBUTE, true);$response = $client->sendRequest($request);

Чтобы хоть чуть-чуть упростить вам написание обработчика, в пакете поставляется класс Webclient\Fake\Handler\SimpleRoutingHandler - обработчик с примитивным роутингом.

<?phpuse Webclient\Fake\Client;use Webclient\Fake\Handler\SimpleRoutingHandler;use Psr\Http\Message\RequestInterface;use Psr\Http\Server\RequestHandlerInterface;/**  * @var RequestHandlerInterface $notFoundHandler Обработчик запросов,  *      для которых не нашлось роута. * @var RequestHandlerInterface $entityCreatedHandler Обработчик запросов *      для эмуляции созания сущности (POST /entities). * @var RequestHandlerInterface $entityHandler Обработчик запросов *      для получения сущности (GET /entities/1). * @var RequestHandlerInterface $entityDeletedHandler обработчик запросов *      для удаления сущности (DELETE /entities/2). * @var RequestInterface $errorRequest Запрос несуществующего URI (GET /users). * @var RequestInterface $entityCreatingRequest Запрос создания  *      сущности (POST /entities). * @var RequestInterface $entityRequest Запрос получения  *      сущности (GET /entities/1). * @var RequestInterface $entityDeletingRequest Запрос удаления  *      сущности (DELETE /entities/2). */$handler = new SimpleRoutingHandler($notFoundHandler);$handler    ->route(['GET', 'HEAD'], '/entities/1', $entityHandler)    ->route(['POST'], '/entities', $entityCreatedHandler)    ->route(['DELETE'], '/entities/2', $entityDeletedHandler);$client = new Client($handler);$resp1 = $client->sendRequest($errorRequest); // Вернёт ошибку 404$resp2 = $client->sendRequest($entityCreatingRequest); // Вернёт ответс кодом 201$resp3 = $client->sendRequest($entityRequest); // Вернёт ответ с кодом 200$resp4 = $client->sendRequest($entityDeletingRequest); // Вернёт ответ с кодом 204

Надеюсь, вы не зря потратили время на прочтение этой статьи и кому-то приглянутся предоставленные инструменты. Спасибо за внимание!

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru