Cliente HTTP
Introducción
Laravel proporciona una API alrededor del cliente HTTP Guzzle, que te permite realizar rápidamente peticiones HTTP salientes para comunicarte con otras aplicaciones web. La envoltura de Laravel alrededor de Guzzle se centra en los casos de uso más comunes y en una maravillosa experiencia para el desarrollador.
Antes de empezar, debes asegurarte de que has instalado el paquete Guzzle como dependencia de tu aplicación. Por defecto, Laravel incluye automáticamente esta dependencia. Sin embargo, si previamente has eliminado el paquete, puedes instalarlo de nuevo a través de Composer:
composer require guzzlehttp/guzzle
Realización de solicitudes
Para realizar peticiones, puedes utilizar los métodos head
, get
, post
, put
, patch
y delete
proporcionados por la facade Http
. Primero, examinemos cómo hacer una petición GET
básica a otra URL:
use Illuminate\Support\Facades\Http; $response = Http::get('http://example.com');
El método get
devuelve una instancia de Illuminate\Http\Client\Response
, que proporciona una variedad de métodos que se pueden utilizar para inspeccionar la respuesta:
$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;
El objeto Illuminate\Http\Client\Response
también implementa la interfaz PHP ArrayAccess
, lo que le permite acceder a los datos de respuesta JSON directamente en la respuesta:
return Http::get('http://example.com/users/1')['name'];
Volcado de peticiones
Si desea volcar la instancia de la petición saliente antes de que sea enviada y terminar la ejecución del script, puede añadir el método dd
al principio de la definición de su petición:
return Http::dd()->get('http://example.com');
Datos de la solicitud
Por supuesto, es común cuando se hacen peticiones POST
, PUT
, y PATCH
enviar datos adicionales con su petición, por lo que estos métodos aceptan un array de datos como segundo argumento. Por defecto, los datos se enviarán utilizando el tipo de contenido application/json
:
use Illuminate\Support\Facades\Http; $response = Http::post('http://example.com/users', [ 'name' => 'Steve', 'role' => 'Network Administrator',]);
Parámetros de consulta de la petición GET
Al realizar peticiones GET
, puede añadir una cadena de consulta a la URL directamente o pasar un array de pares clave/valor como segundo argumento al método get
:
$response = Http::get('http://example.com/users', [ 'name' => 'Taylor', 'page' => 1,]);
Envío de solicitudes codificadas con URL de formulario
Si desea enviar datos utilizando el tipo de contenido application/x-www-form-urlencoded
, debe llamar al método asForm
antes de realizar la petición:
$response = Http::asForm()->post('http://example.com/users', [ 'name' => 'Sara', 'role' => 'Privacy Consultant',]);
Envío de un cuerpo de solicitud sin procesar
Puede utilizar el método withBody
si desea proporcionar un cuerpo de petición sin procesar al realizar una petición. El tipo de contenido puede ser proporcionado a través del segundo argumento del método:
$response = Http::withBody( base64_encode($photo), 'image/jpeg')->post('http://example.com/photo');
Peticiones Multi-Part
Si desea enviar archivos como peticiones multi-part, debe llamar al método attach
antes de realizar la petición. Este método acepta el nombre del archivo y su contenido. Si es necesario, puede proporcionar un tercer argumento que se considerará el nombre del fichero:
$response = Http::attach( 'attachment', file_get_contents('photo.jpg'), 'photo.jpg')->post('http://example.com/attachments');
En lugar de pasar el contenido en bruto de un archivo, puede pasar un recurso de flujo:
$photo = fopen('photo.jpg', 'r'); $response = Http::attach( 'attachment', $photo, 'photo.jpg')->post('http://example.com/attachments');
Cabeceras
Se pueden añadir cabeceras a las peticiones utilizando el método withHeaders
. Este método withHeaders
acepta un array de pares clave / valor:
$response = Http::withHeaders([ 'X-First' => 'foo', 'X-Second' => 'bar'])->post('http://example.com/users', [ 'name' => 'Taylor',]);
Puede utilizar el método accept
para especificar el tipo de contenido que su aplicación espera como respuesta a su petición:
$response = Http::accept('application/json')->get('http://example.com/users');
Para mayor comodidad, puede utilizar el método acceptJson
para especificar rápidamente que su aplicación espera el tipo de contenido application/json
en respuesta a su solicitud:
$response = Http::acceptJson()->get('http://example.com/users');
Autenticación
Puedes especificar credenciales de autenticación básica y digest usando los métodos withBasicAuth
y withDigestAuth
, respectivamente:
// Basic authentication...$response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(/* ... */); // Digest authentication...$response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(/* ... */);
Bearer Tokens
Si desea añadir rápidamente un token "bearer" a la cabecera Authorization
de la petición, puede utilizar el método withToken
:
$response = Http::withToken('token')->post(/* ... */);
Tiempo de espera
El método timeout
se puede utilizar para especificar el número máximo de segundos que se debe esperar para recibir una respuesta:
$response = Http::timeout(3)->get(/* ... */);
Si se supera el tiempo de espera dado, se lanzará una instancia de Illuminate\Http\Client\ConnectionException
.
Puede especificar el número máximo de segundos a esperar mientras intenta conectarse a un servidor utilizando el método connectTimeout
:
$response = Http::connectTimeout(3)->get(/* ... */);
Reintentos
Si desea que el cliente HTTP reintente automáticamente la petición si se produce un error en el cliente o en el servidor, puede utilizar el método retry
. El método retry
acepta el número máximo de veces que debe intentarse la petición y el número de milisegundos que Laravel debe esperar entre intentos:
$response = Http::retry(3, 100)->post(/* ... */);
Si es necesario, puedes pasar un tercer argumento al método retry
. El tercer argumento debe ser un callable que determine si los reintentos deben ser realmente intentados. Por ejemplo, puede que sólo desee reintentar la petición si la petición inicial encuentra una ConnectionException
:
$response = Http::retry(3, 100, function ($exception, $request) { return $exception instanceof ConnectionException;})->post(/* ... */);
Si un intento de petición falla, puede que desee realizar un cambio en la petición antes de que se realice un nuevo intento. Esto se puede conseguir modificando el argumento de petición proporcionado a la llamada al método retry
. Por ejemplo, puede que quieras reintentar la petición con un nuevo token de autorización si el primer intento devolvió un error de autenticación:
$response = Http::withToken($this->getToken())->retry(2, 0, function ($exception, $request) { if (! $exception instanceof RequestException || $exception->response->status() !== 401) { return false; } $request->withToken($this->getNewToken()); return true;})->post(/* ... */);
Si todas las peticiones fallan, se lanzará una instancia de Illuminate\Http\Client\RequestException
. Si desea desactivar este comportamiento, puede proporcionar un argumento throw
con el valor false
. Cuando se deshabilita, se devolverá la última respuesta recibida por el cliente después de que se hayan intentado todos los reintentos:
$response = Http::retry(3, 100, throw: false)->post(/* ... */);
Advertencia
Si todas las peticiones fallan debido a un problema de conexión, se lanzará unaexcepción Illuminate\Http\Client\ConnectionException
incluso cuando el argumentothrow
tenga el valorfalse
.
Manejo de Errores
A diferencia del comportamiento por defecto de Guzzle, la envoltura de cliente HTTP de Laravel no lanza excepciones en errores de cliente o servidor (respuestas con codigos 400
y 500
). Puedes determinar si uno de estos errores fue devuelto usando los métodos successful
, clientError
, o serverError
:
// Determine if the status code is >= 200 and < 300...$response->successful(); // Determine if the status code is >= 400...$response->failed(); // Determine if the response has a 400 level status code...$response->clientError(); // Determine if the response has a 500 level status code...$response->serverError(); // Immediately execute the given callback if there was a client or server error...$response->onError(callable $callback);
Lanzar excepciones
Si tiene una instancia de respuesta y desea lanzar una instancia de Illuminate\Http\Client\RequestException
si el código de estado de la respuesta indica un error del cliente o del servidor, puede utilizar los métodos throw
o throwIf
:
$response = Http::post(/* ... */); // Throw an exception if a client or server error occurred...$response->throw(); // Throw an exception if an error occurred and the given condition is true...$response->throwIf($condition); // Throw an exception if an error occurred and the given closure resolves to true...$response->throwIf(fn ($response) => true); // Throw an exception if an error occurred and the given condition is false...$response->throwUnless($condition); // Throw an exception if an error occurred and the given closure resolves to false...$response->throwUnless(fn ($response) => false); return $response['user']['id'];
La instancia Illuminate\Http\Client\RequestException
tiene una propiedad pública $response
que le permitirá inspeccionar la respuesta devuelta.
El método throw
devuelve la instancia de respuesta si no se ha producido ningún error, lo que permite encadenar otras operaciones con el método throw
:
return Http::post(/* ... */)->throw()->json();
Si desea realizar alguna lógica adicional antes de que se lance la excepción, puede pasar un closure al método throw
. La excepción se lanzará automáticamente después de que se invoque el closure, por lo que no es necesario volver a lanzar la excepción desde dentro del closure:
return Http::post(/* ... */)->throw(function ($response, $e) { //})->json();
Guzzle middleware
Dado que el cliente HTTP de Laravel se basa en Guzzle, usted puede sacar provecho del Guzzle Middleware para manipular la solicitud saliente o inspeccionar la respuesta entrante. Para manipular la petición saliente, registra un middleware Guzzle a través del método withMiddleware
en combinación con la factoría de middleware mapRequest
de Guzzle:
use GuzzleHttp\Middleware;use Illuminate\Support\Facades\Http;use Psr\Http\Message\RequestInterface; $response = Http::withMiddleware( Middleware::mapRequest(function (RequestInterface $request) { $request->withHeader('X-Example', 'Value'); return $request; }))->get('http://example.com');
Del mismo modo, puedes inspeccionar la respuesta HTTP entrante registrando un middleware a través del método withMiddleware
en combinación con la factoría de middleware mapResponse
de Guzzle:
use GuzzleHttp\Middleware;use Illuminate\Support\Facades\Http;use Psr\Http\Message\ResponseInterface; $response = Http::withMiddleware( Middleware::mapResponse(function (ResponseInterface $response) { $header = $response->getHeader('X-Example'); // ... return $response; }))->get('http://example.com');
Opciones de Guzzle
Puede especificar opciones de petición Guzzle adicionales usando el método withOptions
. El método withOptions
acepta un array de pares clave / valor:
$response = Http::withOptions([ 'debug' => true,])->get('http://example.com/users');
Peticiones concurrentes
A veces, es posible que desees hacer varias peticiones HTTP simultáneamente. En otras palabras, quieres que varias peticiones se envíen al mismo tiempo en lugar de emitir las peticiones secuencialmente. Esto puede mejorar sustancialmente el rendimiento cuando se interactúa con APIs HTTP lentas.
Afortunadamente, puedes conseguirlo usando el método pool
. El método pool
acepta un closure que recibe una instancia Illuminate\Http\Client\Pool
, lo que le permite agregar fácilmente las solicitudes al pool de solicitud para que sean despachadas:
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();
Como puede ver, se puede acceder a cada instancia de respuesta basándose en el orden en que fue añadida al pool. Si lo desea, puede nombrar las peticiones utilizando el método as
, que le permite acceder a las respuestas correspondientes por su nombre:
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();
Macros
El cliente HTTP de Laravel permite definir "macros", que pueden servir como un mecanismo fluido y expresivo para configurar rutas de petición y cabeceras comunes al interactuar con servicios en toda la aplicación. Para empezar, puede definir la macro dentro del método boot
de la clase AppProviders\AppServiceProvider
de su aplicación:
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'); });}
Una vez configurada tu macro, puedes invocarla desde cualquier parte de tu aplicación para crear una petición pendiente con la configuración especificada:
$response = Http::github()->get('/');
Testing
Muchos servicios de Laravel proporcionan funcionalidades para ayudarte a escribir tests forma fácil y expresiva, y el cliente HTTP de Laravel no es una excepción. El método fake
de la facade Http
permite instruir al cliente HTTP para que devuelva respuestas stubbed / dummy cuando se realizan peticiones.
Falsificación de respuestas
Por ejemplo, para indicar al cliente HTTP que devuelva respuestas vacías, con código de estado 200
para cada petición, puedes llamar al método fake
sin argumentos:
use Illuminate\Support\Facades\Http; Http::fake(); $response = Http::post(/* ... */);
Falsificación de URL específicas
De manera alternativa, puede pasar un array al método fake
. Las claves del array deben representar los patrones de URL que desea falsificar y sus respuestas asociadas. El carácter *
puede utilizarse como comodín. Cualquier petición realizada a una URL que no haya sido falsificada será ejecutada. Puede utilizar el método response
de la facade Http
para construir respuestas stub / falsas para estos endpoints:
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),]);
Si desea especificar un patrón de URL alternativa que sea usado en todas las URL no coincidentes, puede utilizar un único carácter *
:
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']),]);
Falsificación de secuencias de respuesta
A veces puede que necesite especificar que una única URL devuelva una serie de respuestas falsas en un orden específico. Puede conseguirlo utilizando el método Http::sequence
para construir las respuestas:
Http::fake([ // Stub a series of responses for GitHub endpoints... 'github.com/*' => Http::sequence() ->push('Hello World', 200) ->push(['foo' => 'bar'], 200) ->pushStatus(404),]);
Cuando se hayan consumido todas las respuestas de una secuencia de respuesta, cualquier otra petición hará que la secuencia de respuesta lance una excepción. Si desea especificar una respuesta por defecto que debe ser devuelta cuando una secuencia está vacía, puede utilizar el método 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()),]);
Si desea falsificar una secuencia de respuestas pero no necesita especificar un patrón de URL concreto que deba falsificarse, puede utilizar el método Http::fakeSequence
:
Http::fakeSequence() ->push('Hello World', 200) ->whenEmpty(Http::response());
Fake Callback
Si necesita una lógica más complicada para determinar qué respuestas devolver para ciertos puntos finales, puede pasar un closure al método false
. Este closure recibirá una instancia de Illuminate\Http\Client\Request
y debe devolver una instancia de respuesta. Dentro de su closure, puede realizar cualquier lógica que sea necesaria para determinar qué tipo de respuesta devolver:
use Illuminate\Http\Client\Request; Http::fake(function (Request $request) { return Http::response('Hello World', 200);});
Prevención de Peticiones Perdidas
Si desea asegurarse de que todas las solicitudes enviadas a través del cliente HTTP han sido falsificadas a lo largo de su test individual o conjunto de test completo, puede llamar al método preventStrayRequests
. Después de llamar a este método, cualquier petición que no tenga una respuesta falsa correspondiente lanzará una excepción en lugar de realizar la petición HTTP real:
use Illuminate\Support\Facades\Http; Http::preventStrayRequests(); Http::fake([ 'github.com/*' => Http::response('ok'),]); // An "ok" response is returned...Http::get('https://github.com/laravel/framework'); // An exception is thrown...Http::get('https://laravel.com');
Inspección de peticiones
Cuando se falsifican respuestas, puede que ocasionalmente desee inspeccionar las peticiones que recibe el cliente para asegurarse de que su aplicación está enviando los datos o cabeceras correctos. Puede hacerlo llamando al método Http::assertSent
después de llamar a Http::fake
.
El método assertSent
acepta un closure que recibirá una instancia Illuminate\Http\Client\Request
y debe devolver un valor booleano que indica si la solicitud coincide con sus expectativas. Para que el test pase, se debe haber emitido al menos una solicitud que coincida con las expectativas dadas:
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';});
Si es necesario, puedes afirmar que una petición específica no fue enviada usando el método 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';});
Puede utilizar el método assertSentCount
para comprobar cuántas solicitudes se han "enviado" durante la test:
Http::fake(); Http::assertSentCount(5);
O puede utilizar el método assertNothingSent
para afirmar que no se ha enviado ninguna petición durante la test:
Http::fake(); Http::assertNothingSent();
Registro de Peticiones / Respuestas
Puede utilizar el método recorded
para recopilar todas las solicitudes y sus correspondientes respuestas. El método recorded
devuelve una colección de arrays que contiene instancias de Illuminate\Http\Client\Request
y Illuminate\Http\Client\Response
:
Http::fake([ 'https://laravel.com' => Http::response(status: 500), 'https://nova.laravel.com/' => Http::response(),]); Http::get('https://laravel.com');Http::get('https://nova.laravel.com/'); $recorded = Http::recorded(); [$request, $response] = $recorded[0];
Además, el método recorded
acepta un closure que recibirá una instancia de Illuminate\Http\Client\Request
y Illuminate\Http\Client\Response
y se puede utilizar para filtrar pares de solicitud / respuesta en función de sus expectativas:
use Illuminate\Http\Client\Request;use Illuminate\Http\Client\Response; Http::fake([ 'https://laravel.com' => Http::response(status: 500), 'https://nova.laravel.com/' => Http::response(),]); Http::get('https://laravel.com');Http::get('https://nova.laravel.com/'); $recorded = Http::recorded(function (Request $request, Response $response) { return $request->url() !== 'https://laravel.com' && $response->successful();});
Eventos
Laravel dispara tres eventos durante el proceso de envío de peticiones HTTP. El evento RequestSending
se dispara antes de que se envíe una petición, mientras que el evento ResponseReceived
se dispara después de que se reciba una respuesta para una petición dada. El evento ConnectionFailed
se dispara si no se recibe respuesta para una petición dada.
Los eventos RequestSending
y ConnectionFailed
contienen una propiedad pública $request
que puede utilizar para inspeccionar la instancia Illuminate\Http\Client\Request
. Del mismo modo, el evento ResponseReceived
contiene una propiedad $request
, así como una propiedad $response
que se puede utilizar para inspeccionar la instancia Illuminate\Http\Client\Response
. Puede registrar escuchadores de eventos para este evento en su proveedor de servicios AppProviders\EventServiceProvider
:
/** * The event listener mappings for the application. * * @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', ],];