Guzzle обработка ошибок

The question was:

I would like to handle errors from Guzzle when the server returns 4xx and 5xx status codes

While you can handle 4xx or 5xx status codes specifically, in practice it makes sense to catch all exceptions and handle the results accordingly.

The question is also, whether you just want to handle the errors or get the body? I think in most cases it would be sufficient to handle the errors and not get the message body or only get the body in the case of a non-error.

I would look at the documentation to check how your version of Guzzle handles it because this may change: https://docs.guzzlephp.org/en/stable/quickstart.html#exceptions

Also see this page in the official documentation on «Working with errors», which states:

Requests that receive a 4xx or 5xx response will throw a Guzzle\Http\Exception\BadResponseException. More specifically, 4xx errors throw a Guzzle\Http\Exception\ClientErrorResponseException, and 5xx errors throw a Guzzle\Http\Exception\ServerErrorResponseException. You can catch the specific exceptions or just catch the BadResponseException to deal with either type of error.

Guzzle 7 (from the docs):

. \RuntimeException
└── TransferException (implements GuzzleException)
    └── RequestException
        ├── BadResponseException
        │   ├── ServerException
        │   └── ClientException
        ├── ConnectException
        └── TooManyRedirectsException

So, your code might look like this:

use GuzzleHttp\Exception\TooManyRedirectsException;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\Exception\ConnectException;

// ...

try {
    $response = $client->request('GET', $url);
    if ($response->getStatusCode() >= 300) {
       // is HTTP status code (for non-exceptions) 
       $statusCode = $response->getStatusCode();
       // handle error 
    } else {
      // is valid URL
    }
            
} catch (TooManyRedirectsException $e) {
    // handle too many redirects
} catch (ClientException | ServerException $e) {
    // ClientException is thrown for 400 level errors if the http_errors request option is set to true.
    // ServerException is thrown for 500 level errors if the http_errors request option is set to true.
    if ($e->hasResponse()) {
       // is HTTP status code, e.g. 500 
       $statusCode = $e->getResponse()->getStatusCode();
    }
} catch (ConnectException $e) {
    // ConnectException is thrown in the event of a networking error.
    // This may be an error reported by lowlevel functionality 
    // (e.g.  cURL error)
    $handlerContext = $e->getHandlerContext();
    if ($handlerContext['errno'] ?? 0) {
       // this is the lowlevel error code, not the HTTP status code!!!
       // for example 6 for "Couldn't resolve host" (for libcurl)
       $errno = (int)($handlerContext['errno']);
    } 
    // get a description of the error
    $errorMessage = $handlerContext['error'] ?? $e->getMessage();
         
} catch (\Exception $e) {
    // fallback, in case of other exception
}

If you really need the body, you can retrieve it as usual:

https://docs.guzzlephp.org/en/stable/quickstart.html#using-responses

$body = $response->getBody();

Under the hood, by default cURL is used or PHP stream wrapper, see Guzzle docs, so the error codes and messages may reflect that:

Guzzle no longer requires cURL in order to send HTTP requests. Guzzle will use the PHP stream wrapper to send HTTP requests if cURL is not installed. Alternatively, you can provide your own HTTP handler used to send requests. Keep in mind that cURL is still required for sending concurrent requests.


  • Guzzle exceptions
  • libcurl error codes
  • HTTP status codes

Эта страница содержит краткое введение в Guzzle и вводные примеры. Если вы еще не установили его, рекомендую обратиться к странице Установка Guzzle.

  • Как делать запросы

  • Создание клиента

  • Отправка запросов

  • Асинхронные запросы

  • Параллельные запросы

  • Использование ответов

  • Параметры строки запроса

  • Загрузка данных

  • POST/Form запросы

  • Отправка полей формы

  • Отправка файлов форм

  • Cookies

  • Редиректы (перенаправления)

  • Исключения при обработке ошибок

  • Переменные окружения

  • Соответствующие настройки php.ini

Как делать запросы

Можно делать запросы в Guzzle, используя объект GuzzleHttp\ClientInterface:

Создание клиента

use GuzzleHttp\Client;

$client = new Client([
    // Базовый URI используется с относительными запросами
    'base_uri' => 'http://httpbin.org',
    // Можно установить любое количество параметров запроса по умолчанию.
    'timeout'  => 2.0,
]);

Клиенты являются неизменными в Guzzle 6, что означает, что вы не можете изменить значения по умолчанию, используемые клиентом после его создания.

Конструктор клиента принимает ассоциативный массив опций:

base_uri
(string|UriInterface) Базовый URI клиента, который объединен в относительные URI. Может быть строкой или экземпляром UriInterface. Когда клиенту предоставляется относительный URI, клиент объединяет базовый URI с относительным URI, используя правила, описанные в RFC 3986, section 2.

// Создать клиента с базовым URI
$client = new GuzzleHttp\Client(['base_uri' => 'https://foo.com/api/']);

// Отправить запрос на https://foo.com/api/test
$response = $client->request('GET', 'test');

// Отправить запрос на https://foo.com/root
$response = $client->request('GET', '/root');

Не хочется читать RFC 3986? Вот несколько быстрых примеров того, как base_uri разрешается с другим URI:

base_uri URI Результат
http://foo.com /bar http://foo.com/bar
http://foo.com/foo /bar http://foo.com/bar
http://foo.com/foo bar http://foo.com/bar
http://foo.com/foo/ bar http://foo.com/foo/bar
http://foo.com http://baz.com http://baz.com
http://foo.com/?bar bar http://foo.com/bar
handler
(callable) Функция, которая передает HTTP-запросы по сети. Функция вызывается с Psr7\Http\Message\RequestInterface и массив опций передачи, и должен возвращать GuzzleHttp\Promise\PromiseInterface который выполняется с Psr7\Http\Message\ResponseInterface в случае успеха. handler это опция только для конструктора, которая не может быть переопределена в параметрах per/request.
(mixed) Все остальные параметры, передаваемые конструктору, используются в качестве параметров запроса по умолчанию для каждого запроса, создаваемого клиентом.

Отправка запросов

Магические методы на клиенте позволяют легко отправлять синхронные запросы:

$response = $client->get('http://httpbin.org/get');
$response = $client->delete('http://httpbin.org/delete');
$response = $client->head('http://httpbin.org/get');
$response = $client->options('http://httpbin.org/get');
$response = $client->patch('http://httpbin.org/patch');
$response = $client->post('http://httpbin.org/post');
$response = $client->put('http://httpbin.org/put');

Вы можете создать запрос и затем отправить запрос клиенту, когда будете готовы:

use GuzzleHttp\Psr7\Request;

$request = new Request('PUT', 'http://httpbin.org/put');
$response = $client->send($request, ['timeout' => 2]);

Клиентские объекты обеспечивают большую гибкость в том, как передаются запросы, включая параметры запроса по умолчанию, промежуточное программное обеспечение стека обработчиков по умолчанию, которое используется каждым запросом, и базовый URI, который позволяет отправлять запросы с относительными URI.

Вы можете узнать больше о клиентском промежуточном программном обеспечении на странице Обработчики и связующее ПО документации.

Асинхронные запросы

Вы можете отправлять асинхронные запросы, используя магические методы, предоставляемые клиентом:

$promise = $client->getAsync('http://httpbin.org/get');
$promise = $client->deleteAsync('http://httpbin.org/delete');
$promise = $client->headAsync('http://httpbin.org/get');
$promise = $client->optionsAsync('http://httpbin.org/get');
$promise = $client->patchAsync('http://httpbin.org/patch');
$promise = $client->postAsync('http://httpbin.org/post');
$promise = $client->putAsync('http://httpbin.org/put');

Вы также можете использовать методы sendAsync() и requestAsync() клиента:

// Создать объект запроса PSR-7 для отправки
$headers = ['X-Foo' => 'Bar'];
$body = 'Hello!';
$request = new Request('HEAD', 'http://httpbin.org/head', $headers, $body);
$promise = $client->sendAsync($request);

// Или, если вам не нужно передавать экземпляр запроса:
$promise = $client->requestAsync('GET', 'http://httpbin.org/get');

$promise, возвращаемые этими методами, реализуют Promises/A+ spec, предоставленные библиотекой Guzzle promises. Это означает, что вы сможете связать цепочкой then() вызовы promise. Эти then затем реализуют либо успешный результат с помощью Psr\Http\Message\ResponseInterface, либо запрос отклоняется с выбросом исключения exception:

use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Exception\RequestException;

$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
$promise->then(
    function (ResponseInterface $res) {
        echo $res->getStatusCode() . "\n";
    },
    function (RequestException $e) {
        echo $e->getMessage() . "\n";
        echo $e->getRequest()->getMethod();
    }
);

Параллельные запросы

Вы можете отправлять несколько запросов одновременно, используя Promise и асинхронные запросы:

use GuzzleHttp\Client;
use GuzzleHttp\Promise;

$client = new Client(['base_uri' => 'http://httpbin.org/']);

// Инициируем каждый запрос, но не блокируем
$promises = [
    'image' => $client->getAsync('/image'),
    'png'   => $client->getAsync('/image/png'),
    'jpeg'  => $client->getAsync('/image/jpeg'),
    'webp'  => $client->getAsync('/image/webp')
];

// Дождаться завершения всех запросов. Выдает исключение ConnectException
// если какой-либо из запросов не выполнен
$results = Promise\unwrap($promises);

// Дождемся завершения запросов, даже если некоторые из них завершатся неудачно
$results = Promise\settle($promises)->wait();

// Можно получить доступ к каждому результату, используя key, предоставленный для функции развертки
echo $results['image']['value']->getHeader('Content-Length')[0];
echo $results['png']['value']->getHeader('Content-Length')[0];

Можно использовать GuzzleHttp\Pool объект, когда у вас есть неопределенное количество запросов, которые вы хотите отправить:

use GuzzleHttp\Pool;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;

$client = new Client();

$requests = function ($total) {
    $uri = 'http://127.0.0.1:8126/guzzle-server/perf';
    for ($i = 0; $i < $total; $i++) {
        yield new Request('GET', $uri);
    }
};

$pool = new Pool($client, $requests(100), [
    'concurrency' => 5,
    'fulfilled' => function ($response, $index) {
        // Тут обработка успешного запроса
    },
    'rejected' => function ($reason, $index) {
        // Тут обработка запроса, который завершился неудачей
    },
]);

// Инициализируем трансферы и создаём promise
$promise = $pool->promise();

// Ожидаем ответ promise // Принудительно завершаем пул запросов
$promise->wait();

Или используя замыкание, которое вернет promise, как только пул вызовет замыкание:

$client = new Client();

$requests = function ($total) use ($client) {
    $uri = 'http://127.0.0.1:8126/guzzle-server/perf';
    for ($i = 0; $i < $total; $i++) {
        yield function() use ($client, $uri) {
            return $client->getAsync($uri);
        };
    }
};

$pool = new Pool($client, $requests(100));

Использование ответов

В предыдущих примерах мы получили $response или мы получили ответ от promise. Объект ответа реализует ответ PSR-7, Psr\Http\Message\ResponseInterface, и содержит много полезной информации.

Вы можете получить код состояния и фразу ответа:

$code = $response->getStatusCode(); // 200
$reason = $response->getReasonPhrase(); // OK

Вы можете извлечь заголовки из ответа:

// Проверка, есть ли заголовок
if ($response->hasHeader('Content-Length')) {
    echo "It exists";
}

// Получаем заголовок из ответа
echo $response->getHeader('Content-Length')[0];

// Получаем все заголовки ответа
foreach ($response->getHeaders() as $name => $values) {
    echo $name . ': ' . implode(', ', $values) . "\r\n";
}

Тело ответа может быть получено с помощью метода getBody. Тело ответа может быть использовано как строка, приведено к строке или как объект, подобный потоку:

$body = $response->getBody();

// Неявно приводим тело ответа к строке и отображаем его
echo $body;

// Явное приведение тела к строке
$stringBody = (string) $body;

// Читаем первые 10 байт тела ответа
$tenBytes = $body->read(10);

// Прочитать оставшееся содержимое тела ответа как строку
$remainingBytes = $body->getContents();

Параметры строки запроса

Вы можете предоставить параметры строки запроса несколькими способами.

Можно установить параметры строки запроса в URI запроса:

$response = $client->request('GET', 'http://httpbin.org?foo=bar');

Можно указать параметры строки запроса, используя query как массив:

$client->request('GET', 'http://httpbin.org', [
    'query' => ['foo' => 'bar']
]);

Предоставление опции в качестве массива будет использовать нативную функцию PHP http_build_query для форматирования строки с параметрами.

И, наконец, можно предоставить query в роли строки:

$client->request('GET', 'http://httpbin.org', ['query' => 'foo=bar']);

Загрузка данных

Guzzle предоставляет несколько способов загрузки данных.

Можно отправлять запросы, содержащие поток данных, передавая строку, ресурс возвращается из fopen, или как экземпляр Psr\Http\Message\StreamInterface в опцию запроса body:

// Предоставляем тело запроса в виде строки
$r = $client->request('POST', 'http://httpbin.org/post', [
    'body' => 'raw data'
]);

// Предоставляем fopen ресурс
$body = fopen('/path/to/file', 'r');
$r = $client->request('POST', 'http://httpbin.org/post', ['body' => $body]);

// Используем функцию stream_for() для создания потока PSR-7
$body = \GuzzleHttp\Psr7\stream_for('hello!');
$r = $client->request('POST', 'http://httpbin.org/post', ['body' => $body]);

Простой способ загрузить данные JSON и установить соответствующий заголовок — использовать опцию запроса json:

$r = $client->request('PUT', 'http://httpbin.org/put', [
    'json' => ['foo' => 'bar']
]);

POST/Form запросы

В дополнение к указанию необработанных данных запроса с использованием опции запросаbody, Guzzle предоставляет полезные абстракции при отправке данных POST.

Отправка полей формы

Для отправки POST-запросов application/x-www-form-urlencoded необходимо указать поля POST в виде массива в параметрах запроса form_params:

$response = $client->request('POST', 'http://httpbin.org/post', [
    'form_params' => [
        'field_name' => 'abc',
        'other_field' => '123',
        'nested_field' => [
            'nested' => 'hello'
        ]
    ]
]);

Отправка файлов форм

Можно отправлять файлы вместе с формой (POST-запросы multipart/form-data), используя опцию запроса multipart. Она принимает массив ассоциативных массивов, где каждый ассоциативный массив содержит следующие ключи:

  • name: (required, string) имя ключа поля формы.
  • contents: (required, mixed) Укажите тут строку для отправки содержимого файла в виде строки, предоставьте ресурс fopen для потоковой передачи содержимого из потока PHP или укажите Psr\Http\Message\StreamInterface для потоковой передачи содержимого из потока PSR-7.
$response = $client->request('POST', 'http://httpbin.org/post', [
    'multipart' => [
        [
            'name'     => 'field_name',
            'contents' => 'abc'
        ],
        [
            'name'     => 'file_name',
            'contents' => fopen('/path/to/file', 'r')
        ],
        [
            'name'     => 'other_file',
            'contents' => 'hello',
            'filename' => 'filename.txt',
            'headers'  => [
                'X-Foo' => 'this is an extra header to include'
            ]
        ]
    ]
]);

Cookies

Guzzle может поддерживать для вас сеанс файлов cookie, если это указано с помощью параметра запроса файлов cookie. При отправке запроса параметр cookie должен быть установлен на экземпляр GuzzleHttp\Cookie\CookieJarInterface:

// Использование конкретного cookie jar
$jar = new \GuzzleHttp\Cookie\CookieJar;
$r = $client->request('GET', 'http://httpbin.org/cookies', [
    'cookies' => $jar
]);

Можно установить для cookie значение true в конструкторе клиента, если хотите использовать общий файл cookie для всех запросов:

// Используем общий клиентский файл cookie
$client = new \GuzzleHttp\Client(['cookies' => true]);
$r = $client->request('GET', 'http://httpbin.org/cookies');

Редиректы (перенаправления)

Guzzle будет автоматически следовать за редиректами, только если чётко не указать этого не делать. Вы можете настроить поведение перенаправления, используя опцию запроса allow_redirects:

  • Установите значение true, чтобы включить нормальные перенаправления с максимальным количеством 5 перенаправлений. Это значение по умолчанию.
  • Установите в false, чтобы отключить перенаправления.
  • Передайте ассоциативный массив, содержащий ключ max, чтобы указать максимальное количество перенаправлений, и при необходимости укажите значение ключа strict, чтобы указать, следует ли использовать строгие перенаправления, совместимые с RFC (что означает запросы перенаправления POST со следующими запросами тоже типа POST, тогда как в обычном режиме большинство браузеров по умолчанию перенаправляют запросы POST со следующими запросами GET).
$response = $client->request('GET', 'http://github.com');
echo $response->getStatusCode();
// 200

В следующем примере показано, как можно отключить редиректы:

$response = $client->request('GET', 'http://github.com', [
    'allow_redirects' => false
]);
echo $response->getStatusCode();
// 301

Исключения при обработке ошибок

Guzzle генерирует исключения для ошибок, возникающих во время передачи:

  • В случае сетевой ошибки (тайм-аут соединения, ошибки DNS и т.д.) выдается сообщение GuzzleHttp\Exception\RequestException. Это исключение распространяется на GuzzleHttp\Exception\TransferException. Поймав это исключение, вы поймете любое исключение, которое может быть сгенерировано при передаче запросов. Пример:
    use GuzzleHttp\Psr7;
    use GuzzleHttp\Exception\RequestException;
    
    try {
        $client->request('GET', 'https://github.com/_abc_123_404');
    } catch (RequestException $e) {
        echo Psr7\str($e->getRequest());
        if ($e->hasResponse()) {
            echo Psr7\str($e->getResponse());
        }
    }
    
  • Исключение GuzzleHttp\Exception\ConnectException выдается в случае сетевой ошибки. Это исключение происходит от GuzzleHttp\Exception\RequestException.
  • GuzzleHttp\Exception\ClientException выбрасывается для ошибок уровня 400, если для параметра запроса http_errors установлено значение true. Это исключение распространяется на GuzzleHttp\Exception\BadResponseException и GuzzleHttp\Exception\BadResponseException распространяется на GuzzleHttp\Exception\RequestException. Пример:
    use GuzzleHttp\Exception\ClientException;
    
    try {
        $client->request('GET', 'https://github.com/_abc_123_404');
    } catch (ClientException $e) {
        echo Psr7\str($e->getRequest());
        echo Psr7\str($e->getResponse());
    }
    
  • GuzzleHttp\Exception\ServerException генерируется для ошибок уровня 500, если для параметра запроса http_errors установлено значение true. Это исключение происходит от GuzzleHttp\Exception\BadResponseException.
  • GuzzleHttp\Exception\TooManyRedirectsException генерируется, когда выполняется слишком много перенаправлений. Это исключение происходит от GuzzleHttp\Exception\RequestException.

Все вышеперечисленные исключения происходят из GuzzleHttp\Exception\TransferException.

Переменные окружения

Guzzle предоставляет несколько переменных среды, которые можно использовать для настройки поведения библиотеки:

GUZZLE_CURL_SELECT_TIMEOUT
Управляет продолжительностью в секундах, которую обработчик curl_multi_* будет использовать при выборе обработчика curl, используя curl_multi_select(). Некоторые системы имеют проблемы с реализацией PHP curl_multi_select(), где вызов этой функции всегда приводит к ожиданию максимальной продолжительности времени ожидания.
HTTP_PROXY
Определяет прокси для использования при отправке запросов по протоколу http.

Поскольку переменная HTTP_PROXY может содержать произвольный пользовательский ввод в некоторых (CGI) средах, эта переменная используется только в CLI SAPI. Смотрите https://httpoxy.org для получения дополнительной информации.

HTTPS_PROXY
Определяет прокси для использования при отправке запросов по протоколу https.

Соответствующие настройки php.ini

Guzzle может использовать настройки php.ini при настройке клиентов:

openssl.cafile
Указывает путь на диске к файлу CA (сертификат SSL/TLS безопасности) в формате PEM, который будет использоваться при отправке запросов через «https». Подробнее: https://wiki.php.net/rfc/tls-peer-verification#phpini_defaults

Смотрите далее: Опции запросов Guzzle | Ранее: Обзор и установка Guzzle | Главная по Guzzle

Исходники на GitHub

Документация здесь

Guzzle

Guzzle — это PHP-HTTP-клиент, который упрощает отправку HTTP-запросов и интеграцию с веб-сервисами. Guzzle может отправлять как синхронные, так и асинхронные запросы, используя один и тот же интерфейс. Среди его плюсов: простой интерфейс работы со строкой запросов, использование HTTP-кук (cookies), загрузка данных. Если вы не хотите сами настраивать POST или GET запросы через cURL, то библиотека Guzzle как раз для вас.

Будем использовать Guzzle. Именно в этой версии есть поддержка стандарта PSR-7 — HTTP Message Interface

Установка Guzzle HTTP-клиент

Рекомендуемый способ установки Guzzle — с помощью Composer. В файле composer.json

{
  "require": {
    "guzzlehttp/guzzle": "^7.8"
  }
}

Или из консоли (терминала):

php composer.phar require guzzlehttp/guzzle

Если нужна последняя нестабильная версия пакета:

{
  "require": {
    "guzzlehttp/guzzle": "^7.8@dev"
  }
}

После установки вам понадобится автозагрузчик Composer:

require 'vendor/autoload.php';

Создание клиента

Можно делать запросы в Guzzle, используя объект GuzzleHttp\ClientInterface:

$client = new Client([
  // Базовый URI используется с относительными запросами
  'base_uri' => 'http://httpbin.org',
  // Вы можете установить любое количество параметров запроса по умолчанию.
  'timeout'  => 2.0,
]);

Клиент является неизменными в Guzzle 6, что означает, что вы не можете изменить значения по умолчанию, используемые клиентом после его создания.

Конструктор клиента принимает ассоциативный массив опций:

base_uri

(string|UriInterface) Базовый URI клиента, который объединен в относительные URI. Может быть строкой или экземпляром UriInterface. Когда клиенту предоставляется относительный URI,
клиент объединяет базовый URI с относительным URI, используя правила, описанные в RFC 3986, section 2.

// Создать клиента с базовым URI
$client = new GuzzleHttp\Client(['base_uri' => 'https://foo.com/api/']);

// Отправить запрос на https://foo.com/api/test
$response = $client->request('GET', 'test');

// Отправить запрос на https://foo.com/root
$response = $client->request('GET', '/root');

Не хочется читать RFC 3986? Вот несколько быстрых примеров того, как base_uri разрешается с другим URI:

base_uri URI Результат
http://foo.com /bar http://foo.com/bar
http://foo.com/foo /bar http://foo.com/bar
http://foo.com/foo bar http://foo.com/bar
http://foo.com/foo/ bar http://foo.com/foo/bar
http://foo.com http://baz.com http://baz.com
http://foo.com/?bar bar http://foo.com/bar

handler

(callable) Функция, которая передает HTTP-запросы по сети. Функция вызывается с Psr7\Http\Message\RequestInterface и массив опций передачи, и должен возвращать GuzzleHttp\Promise\PromiseInterface который выполняется с Psr7\Http\Message\ResponseInterface в случае успеха. handler это опция только для конструктора, которая не может быть переопределена в параметрах per/request.

(mixed) Все остальные параметры, передаваемые конструктору, используются в качестве параметров запроса по умолчанию для каждого запроса, создаваемого клиентом.

Отправка запросов

Магические методы на клиенте позволяют легко отправлять синхронные запросы:

$client = new GuzzleHttp\Client();

$response = $client->get('http://httpbin.org/get');
$response = $client->delete('http://httpbin.org/delete');
$response = $client->head('http://httpbin.org/get');
$response = $client->options('http://httpbin.org/get');
$response = $client->patch('http://httpbin.org/patch');
$response = $client->post('http://httpbin.org/post');
$response = $client->put('http://httpbin.org/put');

Вы можете создать запрос и затем отправить запрос клиенту, когда будете готовы:

use GuzzleHttp\Psr7\Request;

$request = new Request('PUT', 'http://httpbin.org/put');
$response = $client->send($request, ['timeout' => 2]);

Клиентские объекты обеспечивают большую гибкость в том, как передаются запросы, включая параметры запроса по умолчанию, промежуточное программное обеспечение стека обработчиков по умолчанию, которое используется каждым запросом, и базовый URI, который позволяет отправлять запросы с относительными URI.

Асинхронные запросы

Вы можете отправлять асинхронные запросы, используя магические методы, предоставляемые клиентом:

$client = new GuzzleHttp\Client();

$promise = $client->getAsync('http://httpbin.org/get');
$promise = $client->deleteAsync('http://httpbin.org/delete');
$promise = $client->headAsync('http://httpbin.org/get');
$promise = $client->optionsAsync('http://httpbin.org/get');
$promise = $client->patchAsync('http://httpbin.org/patch');
$promise = $client->postAsync('http://httpbin.org/post');
$promise = $client->putAsync('http://httpbin.org/put');

Вы также можете использовать методы sendAsync() и requestAsync() клиента:

// Создать объект запроса PSR-7 для отправки
$headers = ['X-Foo' => 'Bar'];
$body = 'Hello!';
$request = new Request('HEAD', 'http://httpbin.org/head', $headers, $body);
$promise = $client->sendAsync($request);

// Или, если вам не нужно передавать экземпляр запроса:
$promise = $client->requestAsync('GET', 'http://httpbin.org/get');

$promise, возвращаемые этими методами, реализуют Promises/A+ spec, предоставленные библиотекой Guzzle promises. Это означает, что вы сможете связать цепочкой then() вызовы promise. Эти then затем реализуют либо успешный результат с помощью Psr\Http\Message\ResponseInterface, либо запрос отклоняется с выбросом исключения exception:

use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Exception\RequestException;

$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
$promise->then(
  function (ResponseInterface $res) {
    echo $res->getStatusCode() . "\n";
  },
  function (RequestException $e) {
  echo $e->getMessage() . "\n";
    echo $e->getRequest()->getMethod();
  }
);

Параллельные запросы

Вы можете отправлять несколько запросов одновременно, используя Promise и асинхронные запросы:

use GuzzleHttp\Client;
use GuzzleHttp\Promise;

$client = new Client(['base_uri' => 'http://httpbin.org/']);

// Инициируем каждый запрос, но не блокируем
$promises = [
  'image' => $client->getAsync('/image'),
  'png'   => $client->getAsync('/image/png'),
  'jpeg'  => $client->getAsync('/image/jpeg'),
  'webp'  => $client->getAsync('/image/webp')
];

// Дождаться завершения всех запросов. Выдает исключение ConnectException
// если какой-либо из запросов не выполнен
$results = Promise\unwrap($promises);

// Дождемся завершения запросов, даже если некоторые из них завершатся неудачно
$results = Promise\settle($promises)->wait();

// Можно получить доступ к каждому результату, используя key, предоставленный для функции развертки
echo $results['image']['value']->getHeader('Content-Length')[0]
echo $results['png']['value']->getHeader('Content-Length')[0]

Можно использовать GuzzleHttp\Pool объект, когда у вас есть неопределенное количество запросов, которые вы хотите отправить:

use GuzzleHttp\Pool;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;

$client = new Client();

$requests = function ($total) {
  $uri = 'http://127.0.0.1:8126/guzzle-server/perf';
  for ($i = 0; $i < $total; $i++) {
    yield new Request('GET', $uri);
  }
};

$pool = new Pool($client, $requests(100), [
  'concurrency' => 5,
  'fulfilled' => function ($response, $index) {
  // Здесь обработка успешного запроса
  },
  'rejected' => function ($reason, $index) {
  // Здесь обработка запроса, который завершился неудачей
  },
]);

// Инициализируем трансферы и создаём promise
$promise = $pool->promise();

// Ожидаем ответ promise // Принудительно завершаем пул запросов
$promise->wait();

Или используя замыкание, которое вернет promise, как только пул вызовет замыкание:

$client = new Client();

$requests = function ($total) use ($client) {
  $uri = 'http://127.0.0.1:8126/guzzle-server/perf';
  for ($i = 0; $i < $total; $i++) {
    yield function() use ($client, $uri) {
      return $client->getAsync($uri);
    };
  }
};

$pool = new Pool($client, $requests(100));

Использование ответов

В предыдущих примерах мы получили $response или мы получили ответ от promise. Объект ответа реализует ответ PSR-7, Psr\Http\Message\ResponseInterface, и содержит много полезной информации.

Вы можете получить код состояния и фразу ответа:

$code = $response->getStatusCode(); // 200
$reason = $response->getReasonPhrase(); // OK

Вы можете извлечь заголовки из ответа:

// Проверка, есть ли заголовок
if ($response->hasHeader('Content-Length')) {
echo "It exists";
}

// Получаем заголовок из ответа
echo $response->getHeader('Content-Length')[0];

// Получаем все заголовки ответа
foreach ($response->getHeaders() as $name => $values) {
echo $name . ': ' . implode(', ', $values) . "\r\n";
}

Тело ответа может быть получено с помощью метода getBody(). Тело ответа может быть использовано как строка, приведено к строке или как объект, подобный потоку:

$body = $response->getBody();

// Неявно приводим тело ответа к строке и отображаем его
echo $body;

// Явное приведение тела к строке
$stringBody = (string) $body;

// Читаем первые 10 байт тела ответа
$tenBytes = $body->read(10);

// Прочитать оставшееся содержимое тела ответа как строку
$remainingBytes = $body->getContents();

Параметры строки запроса

Вы можете предоставить параметры строки запроса несколькими способами. Можно установить параметры строки запроса в URI запроса:

$response = $client->request('GET', 'http://httpbin.org?foo=bar');

Можно указать параметры строки запроса, используя query как массив:

$client->request('GET', 'http://httpbin.org', [
'query' => ['foo' => 'bar']
]);

Предоставление опции в качестве массива будет использовать нативную функцию PHP http_build_query для форматирования строки с параметрами.

И, наконец, можно предоставить query в роли строки:

$client->request('GET', 'http://httpbin.org', ['query' => 'foo=bar']);

Загрузка данных

Guzzle предоставляет несколько способов загрузки данных.

Можно отправлять запросы, содержащие поток данных, передавая строку, ресурс возвращается из fopen, или как экземпляр Psr\Http\Message\StreamInterface в опцию запроса body:

// Предоставляем тело запроса в виде строки
$r = $client->request('POST', 'http://httpbin.org/post', [
  'body' => 'raw data'
]);

// Предоставляем fopen ресурс
$body = fopen('/path/to/file', 'r');
$r = $client->request('POST', 'http://httpbin.org/post', ['body' => $body]);

// Используем функцию stream_for() для создания потока PSR-7
$body = \GuzzleHttp\Psr7\stream_for('hello!');
$r = $client->request('POST', 'http://httpbin.org/post', ['body' => $body]);

Простой способ загрузить данные JSON и установить соответствующий заголовок — использовать опцию запроса json:

$r = $client->request('PUT', 'http://httpbin.org/put', [
  'json' => ['foo' => 'bar']
]);

POST/Form запросы

В дополнение к указанию необработанных данных запроса с использованием опции запроса body, Guzzle предоставляет полезные абстракции при отправке данных POST.

Отправка полей формы

Для отправки POST-запросов application/x-www-form-urlencoded необходимо указать поля POST в виде массива в параметрах запроса form_params:

$response = $client->request('POST', 'http://httpbin.org/post', [
  'form_params' => [
  'field_name' => 'abc',
  'other_field' => '123',
  'nested_field' => [
    'nested' => 'hello'
   ]
  ]
]);

Отправка файлов форм

Можно отправлять файлы вместе с формой (POST-запросы multipart/form-data), используя опцию запроса multipart. Она принимает массив ассоциативных массивов, где каждый ассоциативный массив содержит следующие ключи:

  • name: (required, string) имя ключа поля формы.
  • contents: (required, mixed) Укажите тут строку для отправки содержимого файла в виде строки, предоставьте ресурс fopen для потоковой передачи содержимого из потока PHP или укажите Psr\Http\Message\StreamInterface для потоковой передачи содержимого из потока PSR-7.
$response = $client->request('POST', 'http://httpbin.org/post', [
 'multipart' => [
  [
    'name'     => 'field_name',
    'contents' => 'abc'
  ],
  [
    'name'     => 'file_name',
    'contents' => fopen('/path/to/file', 'r')
  ],
  [
    'name'     => 'other_file',
    'contents' => 'hello',
    'filename' => 'filename.txt',
    'headers'  => [
      'X-Foo' => 'this is an extra header to include'
    ]
  ]
 ]
]);

Cookies

Guzzle может поддерживать для вас сеанс файлов cookie, если это указано с помощью параметра запроса файлов cookie. При отправке запроса параметр cookie должен быть установлен на экземпляр GuzzleHttp\Cookie\CookieJarInterface:

// Использование конкретного cookie jar
$jar = new \GuzzleHttp\Cookie\CookieJar;
$r = $client->request('GET', 'http://httpbin.org/cookies', [
  'cookies' => $jar
]);

Можно установить для cookie значение true в конструкторе клиента, если хотите использовать общий файл cookie для всех запросов:

// Используем общий клиентский файл cookie
$client = new \GuzzleHttp\Client(['cookies' => true]);
$r = $client->request('GET', 'http://httpbin.org/cookies');

Редиректы (перенаправления)

Guzzle будет автоматически следовать за редиректами, только если чётко не указать этого не делать. Вы можете настроить поведение перенаправления, используя опцию запроса allow_redirects:

  • Установите значение true, чтобы включить нормальные перенаправления с максимальным количеством 5 перенаправлений. Это значение по умолчанию.
  • Установите в false, чтобы отключить перенаправления.
  • Передайте ассоциативный массив, содержащий ключ max, чтобы указать максимальное количество перенаправлений, и при необходимости укажите значение ключа strict, чтобы указать, следует ли использовать строгие перенаправления, совместимые с RFC (что означает запросы перенаправления POST со следующими запросами тоже типа POST, тогда как в обычном режиме большинство браузеров по умолчанию перенаправляют запросы POST со следующими запросами GET)./li>
$response = $client->request('GET', 'http://github.com');
echo $response->getStatusCode();
// 200

В следующем примере показано, как можно отключить редиректы:

$response = $client->request('GET', 'http://github.com', [
  'allow_redirects' => false
]);
echo $response->getStatusCode();
// 301

Исключения при обработке ошибок

Guzzle генерирует исключения для ошибок, возникающих во время передачи:

В случае сетевой ошибки (тайм-аут соединения, ошибки DNS и т.д.) выдается сообщение GuzzleHttp\Exception\RequestException. Это исключение распространяется на GuzzleHttp\Exception\TransferException. Поймав это исключение, вы поймете любое исключение, которое может быть сгенерировано при передаче запросов.

use GuzzleHttp\Psr7;
use GuzzleHttp\Exception\RequestException;

try {
  $client->request('GET', 'https://github.com/_abc_123_404');
} catch (RequestException $e) {
  echo Psr7\str($e->getRequest());
  if ($e->hasResponse()) {
    echo Psr7\str($e->getResponse());
  }
}

Исключение GuzzleHttp\Exception\ConnectException выдается в случае сетевой ошибки. Это исключение происходит от GuzzleHttp\Exception\RequestException.
GuzzleHttp\Exception\ClientException выбрасывается для ошибок уровня 400, если для параметра запроса http_errors установлено значение true. Это исключение распространяется на GuzzleHttp\Exception\BadResponseException и GuzzleHttp\Exception\BadResponseException распространяется на GuzzleHttp\Exception\RequestException.

use GuzzleHttp\Exception\ClientException;

try {
  $client->request('GET', 'https://github.com/_abc_123_404');
} catch (ClientException $e) {
  echo Psr7\str($e->getRequest());
  echo Psr7\str($e->getResponse());
}

GuzzleHttp\Exception\ServerException генерируется для ошибок уровня 500, если для параметра запроса http_errors установлено значение true. Это исключение происходит от GuzzleHttp\Exception\BadResponseException.

GuzzleHttp\Exception\TooManyRedirectsException генерируется, когда выполняется слишком много перенаправлений. Это исключение происходит от GuzzleHttp\Exception\RequestException.

Все вышеперечисленные исключения происходят из GuzzleHttp\Exception\TransferException.

Переменные окружения

Guzzle предоставляет несколько переменных среды, которые можно использовать для настройки поведения библиотеки:

GUZZLE_CURL_SELECT_TIMEOUT

Управляет продолжительностью в секундах, которую обработчик curl_multi_* будет использовать при выборе обработчика curl, используя curl_multi_select(). Некоторые системы имеют проблемы с реализацией PHP curl_multi_select(), где вызов этой функции всегда приводит к ожиданию максимальной продолжительности времени ожидания.

HTTP_PROXY

Определяет прокси для использования при отправке запросов по протоколу http.

Поскольку переменная HTTP_PROXY может содержать произвольный пользовательский ввод в некоторых (CGI) средах, эта переменная используется только в CLI SAPI. Смотрите https://httpoxy.org для получения дополнительной информации.

HTTPS_PROXY

Определяет прокси для использования при отправке запросов по протоколу https.

Соответствующие настройки php.ini

Guzzle может использовать настройки php.ini при настройке клиентов:

openssl.cafile

Указывает путь на диске к файлу CA (сертификат SSL/TLS безопасности) в формате PEM, который будет использоваться при отправке запросов через «https». Подробнее: https://wiki.php.net/rfc/tls-peer-verification#phpini_defaults

Опции запросов

Guzzle 3.x

В документах вы можете поймать соответствующий тип исключения ( ClientErrorResponseException для ошибок 4xx) и вызвать его метод getResponse() для получения объекта ответа, а затем вызвать getBody() :

 use Guzzle\Http\Exception\ClientErrorResponseException; ... try { $response = $request->send(); } catch (ClientErrorResponseException $exception) { $responseBody = $exception->getResponse()->getBody(true); } 

Передача true функции getBody означает, что вы хотите получить тело ответа как строку. В противном случае вы получите его как экземпляр класса Guzzle\Http\EntityBody .

Guzzle 6.x

В документах , типы исключений, которые вам могут понадобиться поймать:

  • GuzzleHttp\Exception\ClientException для 400-уровневых ошибок
  • GuzzleHttp\Exception\ServerException для 500-уровневых ошибок
  • GuzzleHttp\Exception\BadResponseException для обоих (это их суперкласс)

Код для обработки таких ошибок теперь выглядит примерно так:

 $client = new GuzzleHttp\Client; try { $client->get('http://google.com/nosuchpage'); } catch (GuzzleHttp\Exception\ClientException $e) { $response = $e->getResponse(); $responseBodyAsString = $response->getBody()->getContents(); } 

Хотя ответы выше хорошо, они не будут иметь дело с сетевыми ошибками, поскольку Mark упомянул BadResponseException – это просто суперкласс для ClientException и ServerException. Но RequestException также является суперклассом BadRequestException. Это приведет не только к 400 и 500 ошибкам, но и к сетевым ошибкам. Итак, скажем, вы запрашиваете страницу ниже, но ваша сеть играет, и ваш улов ожидает исключения BadResponseException. Ну, ваше приложение будет выдавать ошибку.

В этом случае лучше ожидать RequestException и проверить ответ.

 try { $client->get('http://123123123.com') } catch (RequestException $e) { // If there are network errors, we need to ensure the application doesn't crash. // if $e->hasResponse is not null we can attempt to get the message // Otherwise, we'll just pass a network unavailable message. if ($e->hasResponse()) { $exception = (string) $e->getResponse()->getBody(); $exception = json_decode($exception); return new JsonResponse($exception, $e->getCode()); } else { return new JsonResponse($e->getMessage(), 503); } } 
  • Введение
  • Выполнение запросов

    • Данные запроса
    • Заголовки
    • Аутентификация
    • Время ожидания
    • Повторные попытки
    • Обработка ошибок
    • Параметры Guzzle
  • Параллельные запросы
  • Макросы
  • Тестирование

    • Фиктивные ответы
    • Инспектирование запросов
  • События

Введение

Laravel предлагает минимальный и выразительный API для HTTP-клиента Guzzle, позволяющий быстро выполнять исходящие запросы для взаимодействия с другими веб-приложениями. Обертка вокруг Guzzle ориентирована на наиболее распространенные варианты использования и дает прекрасные возможности для разработчиков.

Вы должны убедиться, что пакет Guzzle включен в зависимости вашего приложения. По умолчанию Laravel автоматически включает эту зависимость. Однако, если вы ранее удалили пакет, то вы можете установить его снова через Composer:

composer require guzzlehttp/guzzle

Выполнение запросов

Для отправки запросов вы можете использовать методы head, get, post, put, patch и delete фасада Http. Сначала давайте рассмотрим, как сделать основной запрос GET:

use Illuminate\Support\Facades\Http;

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

Метод get возвращает экземпляр Illuminate\Http\Client\Response, содержащий методы, которые можно использовать для получения информации об ответе:

$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 также реализует интерфейс ArrayAccess PHP, позволяющий напрямую получать доступ к данным ответа JSON:

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

Вывод информации о запросах

Если вы хотите получить информацию о сформированном экземпляре исходящего запроса перед его отправкой и прекратить выполнение скрипта, вы можете добавить метод dd в начало определения вашего запроса:

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

Данные запроса

При выполнении запросов POST, PUT и PATCH обычно отправляются дополнительные данные, поэтому эти методы принимают массив данных в качестве второго аргумента. По умолчанию данные будут отправляться с использованием типа содержимого application/json:

use Illuminate\Support\Facades\Http;

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

Параметры GET-запроса

При выполнении запросов GET вы можете либо напрямую добавить строку запроса к URL, либо передать массив пар ключ / значение в качестве второго аргумента метода get:

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

Отправка запросов с передачей данных в URL-кодированной строке

Если вы хотите отправлять данные с использованием типа содержимого application/x-www-form-urlencoded, то вы должны вызвать метод asForm перед выполнением запроса:

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

Отправка необработанного тела запроса

Вы можете использовать метод withBody, если хотите передать необработанное тело запроса при его выполнении. Тип контента может быть указан вторым аргументом метода:

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

Составные запросы

Если вы хотите отправлять файлы в запросах, состоящих из нескольких частей, необходимо вызвать метод attach перед выполнением запроса. Этот метод принимает имя файла и его содержимое. При желании вы можете указать третий аргумент, который будет считаться именем файла:

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

Вы также можете передать потоковый ресурс вместо передачи необработанного содержимого файла:

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

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

Заголовки

Заголовки могут быть добавлены к запросам с помощью метода withHeaders. Метод withHeaders принимает массив пар ключ / значение:

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

Вы можете использовать метод accept, чтобы указать тип контента, который ваше приложение ожидает в ответ на ваш запрос:

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

Для удобства вы можете использовать метод acceptJson, чтобы быстро указать, что ваше приложение ожидает тип содержимого application/json в ответ на ваш запрос:

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

Аутентификация

Вы можете указать данные basic и digest аутентификации, используя методы withBasicAuth и withDigestAuth, соответственно:

// Basic HTTP-аутентификация ...
$response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(...);

// Digest HTTP-аутентификациия ...
$response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(...);

Токены Bearer

Если вы хотите добавить токен в заголовок Authorization запроса, то используйте метод withToken:

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

Время ожидания

Метод timeout используется для указания максимального количества секунд ожидания ответа:

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

Если указанный тайм-аут превышен, то будет выброшено исключение Illuminate\Http\Client\ConnectionException.

Повторные попытки

Если вы хотите, чтобы HTTP-клиент автоматически повторял запрос при возникновении ошибки клиента или сервера, то используйте метод retry. Метод retry принимает максимальное количество попыток выполнения запроса и количество миллисекунд, которые Laravel должен ждать между попытками:

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

При необходимости вы можете передать третий аргумент методу retry. Третий аргумент должен быть callable-функцией, которая определяет, следует ли на самом деле попытаться повторить попытку. Например, вы можете захотеть повторить запрос только в том случае, если начальный запрос обнаруживает исключение ConnectionException:

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

Если все запросы окажутся неуспешными, то будет выброшено исключение Illuminate\Http\Client\RequestException.

Обработка ошибок

В отличие от поведения Guzzle по умолчанию, обертка HTTP-клиента Laravel не генерирует исключений при возникновении ошибок клиента или сервера (ответы 400 и 500, соответственно). Вы можете определить, была ли возвращена одна из этих ошибок, используя методы successful, clientError, или serverError:

// Определить, имеет ли ответ код состояния >= 200 and < 300...
$response->successful();

// Определить, имеет ли ответ код состояния >= 400...
$response->failed();

// Определить, имеет ли ответ код состояния 400 ...
$response->clientError();

// Определить, имеет ли ответ код состояния 500 ...
$response->serverError();

// Немедленно выполнить данную функцию обратного вызова, если произошла ошибка клиента или сервера...
$response->onError(callable $callback);

Выброс исключений

Если у вас есть экземпляр ответа и вы хотите выбросить исключение Illuminate\Http\Client\RequestException, если код состояния ответа указывает на ошибку клиента или сервера, используйте методы throw или throwIf:

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

// Выбросить исключение, если произошла ошибка клиента или сервера ...
$response->throw();

// Выбросить исключение, если произошла ошибка и данное условие истинно...
$response->throwIf($condition);

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

Экземпляр Illuminate\Http\Client\RequestException имеет свойство $response, которое позволит вам проверить возвращенный ответ.

Метод throw возвращает экземпляр ответа, если ошибки не произошло, что позволяет вам использовать цепочку вызовов после метода throw:

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

Если вы хотите выполнить некоторую дополнительную логику до того, как будет сгенерировано исключение, вы можете передать замыкание методу throw. Исключение будет сгенерировано автоматически после вызова замыкания, поэтому вам не нужно повторно генерировать исключение изнутри замыкания:

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

Параметры Guzzle

Вы можете указать дополнительные параметры запроса для Guzzle, используя метод withOptions. Метод withOptions принимает массив пар ключ / значение:

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

Параллельные запросы

Иногда вы можете захотеть выполнить несколько HTTP-запросов одновременно. Другими словами, вы хотите, чтобы несколько запросов отправлялись одновременно вместо того, чтобы отправлять их последовательно. Это может привести к значительному повышению производительности при взаимодействии с медленными HTTP API.

Вы можете сделать это с помощью метода pool. Метод pool принимает функцию с аргументом Illuminate\Http\Client\Pool, при помощи которого вы можете добавлять запросы в пул запросов для отправки:

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, что позволит вам получить доступ к соответствующим ответам по имени:

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

Макросы

HTTP-клиент Laravel позволяет вам определять «макросы», которые могут служить плавным, выразительным механизмом для настройки общих путей запросов и заголовков при взаимодействии со службами в вашем приложении. Для начала вы можете определить макрос в методе boot класса App\Providers\AppServiceProvider вашего приложения:

use Illuminate\Support\Facades\Http;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Http::macro('github', function () {
        return Http::withHeaders([
            'X-Example' => 'example',
        ])->baseUrl('https://github.com');
    });
}

После того, как ваш макрос настроен, вы можете вызвать его из любого места вашего приложения, чтобы создать ожидающий запрос с указанной конфигурацией:

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

Тестирование

Многие службы Laravel содержат функционал, помогающий вам легко и выразительно писать тесты, и HTTP-клиент Laravel не является исключением. Метод fake фасада Http позволяет указать HTTP-клиенту возвращать заглушенные / фиктивные ответы при выполнении запросов.

Фиктивные ответы

Например, чтобы дать указание HTTP-клиенту возвращать пустые ответы с кодом состояния 200 на каждый запрос, вы можете вызвать метод fake без аргументов:

use Illuminate\Support\Facades\Http;

Http::fake();

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

При фальсификации запросов посредник HTTP-клиента не выполняется. Вы должны определить ожидания для фиктивных ответов, как если бы этот посредник работал правильно.

Фальсификация конкретных URL

В качестве альтернативы вы можете передать массив методу fake. Ключи массива должны представлять шаблоны URL, которые вы хотите подделать, и связанные с ними ответы. Допускается использование метасимвола подстановки *. Любые запросы к URL-адресам, которые не были сфальсифицированы, будут выполнены фактически. Вы можете использовать метод response фасада Http для создания заглушек / фиктивных ответов для этих адресов:

Http::fake([
    // Заглушка JSON ответа для адресов GitHub ...
    'github.com/*' => Http::response(['foo' => 'bar'], 200, $headers),

    // Заглушка строкового ответа для адресов Google ...
    'google.com/*' => Http::response('Hello World', 200, $headers),
]);

Если вы хотите указать шаблон резервного URL-адреса, который будет заглушать все не сопоставленные URL-адреса, то используйте символ *:

Http::fake([
    // Заглушка JSON ответа для адресов GitHub ...
    'github.com/*' => Http::response(['foo' => 'bar'], 200, ['Headers']),

    // Заглушка строкового ответа для всех остальных адресов ...
    '*' => Http::response('Hello World', 200, ['Headers']),
]);

Фальсификация серии ответов

По желанию можно указать, что один URL должен возвращать серию фиктивных ответов в определенном порядке. Вы можете сделать это, используя метод Http::sequence для составления ответов:

Http::fake([
    // Заглушка серии ответов для адресов GitHub ...
    'github.com/*' => Http::sequence()
                            ->push('Hello World', 200)
                            ->push(['foo' => 'bar'], 200)
                            ->pushStatus(404),
]);

Когда все ответы в этой последовательности будут использованы, любые дальнейшие запросы приведут к выбросу исключения. Если вы хотите указать ответ по умолчанию, который должен возвращаться, когда последовательность пуста, то используйте метод whenEmpty:

Http::fake([
    // Заглушка серии ответов для адресов GitHub ...
    '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::assertSent после вызова Http::fake.

Метод assertSent принимает замыкание, которому будет передан экземпляр Illuminate\Http\Client\Request и, которое должно возвращать значение логического типа, указывающее, соответствует ли запрос вашим ожиданиям. Для успешного прохождения теста должен быть отправлен хотя бы один запрос, соответствующий указанным ожиданиям:

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:

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, чтобы определить, сколько запросов было отправлено во время теста:

Http::fake();

Http::assertSentCount(5);

Или используйте метод assertNothingSent, чтобы утверждать, что во время теста не было отправлено никаких запросов:

Http::fake();

Http::assertNothingSent();

События

Laravel запускает три события в процессе отправки HTTP-запросов. Событие RequestSending запускается до отправки запроса, а событие ResponseReceived запускается после получения ответа на данный запрос. Событие ConnectionFailed запускается, если на данный запрос ответ не получен.

События RequestSending и ConnectionFailed содержат общедоступное свойство $request, которое вы можете использовать для проверки экземпляра Illuminate\Http\Client\Request. Аналогично, событие ResponseReceived содержит свойство $request, а также свойство $response, которое может использоваться для проверки экземпляра Illuminate\Http\Client\Response. Вы можете зарегистрировать прослушиватели событий для этого события в вашем сервис-провайдере App\Providers\EventServiceProvider:

/**
 * Сопоставления прослушивателей событий для приложения.
 *
 * @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',
    ],
];

Понравилась статья? Поделить с друзьями:

Интересное по теме:

  • Gutrend style 220 ошибки
  • Hackintosh ошибка при установке
  • H03 ошибка webasto
  • Gutrend style 200 aqua ошибки
  • H03 ошибка bosch

  • Добавить комментарий

    ;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: