Qu’est-ce que le client HTTP
Le client HTTP de Laravel est une API simple qui enveloppe Guzzle .
Via la façade Http, envoyez facilement des requêtes vers des services web externes.
use Illuminate\Support\Facades\ Http ;
$response = Http :: get ( 'https://api.example.com/users' );
Guzzle est préinstallé — aucune configuration nécessaire.
Requêtes de base
GET
use Illuminate\Support\Facades\ Http ;
$response = Http :: get ( 'https://api.example.com/users' );
Paramètres de requête en tableau :
$response = Http :: get ( 'https://api.example.com/users' , [
'page' => 1 ,
'per_page' => 20 ,
]);
POST
Envoi en application/json par défaut.
$response = Http :: post ( 'https://api.example.com/users' , [
'name' => 'Taro Yamada' ,
'email' => '[email protected] ' ,
]);
PUT / PATCH / DELETE
$response = Http :: put ( 'https://api.example.com/users/1' , [
'name' => 'Hanako Yamada' ,
]);
$response = Http :: patch ( 'https://api.example.com/users/1' , [
'email' => '[email protected] ' ,
]);
$response = Http :: delete ( 'https://api.example.com/users/1' );
Traiter la réponse
Http::get() retourne un Illuminate\Http\Client\Response.
$response = Http :: get ( 'https://api.example.com/users/1' );
// Corps
$response -> body (); // Chaîne
$response -> json (); // Tableau
$response -> json ( 'name' ); // Clé précise
$response -> object (); // stdClass
$response -> collect (); // Collection
// Statut
$response -> status ();
$response -> successful (); // 2xx
$response -> failed (); // >= 400
$response -> clientError (); // 4xx
$response -> serverError (); // 5xx
// Codes fréquents
$response -> ok (); // 200
$response -> created (); // 201
$response -> noContent (); // 204
$response -> notFound (); // 404
$response -> unauthorized (); // 401
$response -> forbidden (); // 403
$response -> unprocessableEntity (); // 422
$response -> tooManyRequests (); // 429
Accès par index :
$name = Http :: get ( 'https://api.example.com/users/1' )[ 'name' ];
Options de requête
En-têtes
$response = Http :: withHeaders ([
'X-Api-Version' => '2' ,
'Accept-Language' => 'fr' ,
]) -> get ( 'https://api.example.com/users' );
Pour accepter du JSON, acceptJson().
$response = Http :: acceptJson () -> get ( 'https://api.example.com/users' );
Pour les headers CSRF (X-CSRF-TOKEN / X-XSRF-TOKEN) côté navigateur, voir Protection CSRF .
Authentification
Bearer (le plus courant) :
$response = Http :: withToken ( $token ) -> get ( 'https://api.example.com/me' );
Basic :
$response = Http :: withBasicAuth ( '[email protected] ' , 'password' )
-> get ( 'https://api.example.com/private' );
URL de base
$response = Http :: baseUrl ( 'https://api.example.com' )
-> withToken ( $token )
-> get ( '/users/1' );
application/x-www-form-urlencoded :
$response = Http :: asForm () -> post ( 'https://api.example.com/login' , [
'username' => 'taro' ,
'password' => 'secret' ,
]);
Timeout
// Timeout de réponse (30 s par défaut)
$response = Http :: timeout ( 10 ) -> get ( 'https://api.example.com/slow-endpoint' );
// Timeout de connexion (10 s par défaut)
$response = Http :: connectTimeout ( 5 ) -> get ( 'https://api.example.com/endpoint' );
Un dépassement lève Illuminate\Http\Client\ConnectionException. Toujours définir des timeouts.
Retry
// 3 tentatives max, 100 ms entre
$response = Http :: retry ( 3 , 100 ) -> post ( 'https://api.example.com/orders' , $data );
Retry conditionnel (uniquement erreur de connexion, par exemple) :
use Illuminate\Http\Client\ PendingRequest ;
use Throwable ;
use Illuminate\Http\Client\ ConnectionException ;
$response = Http :: retry ( 3 , 100 , function ( Throwable $exception , PendingRequest $request ) {
return $exception instanceof ConnectionException ;
}) -> post ( 'https://api.example.com/orders' , $data );
Gestion des erreurs
Vérifier manuellement
Par défaut, aucune exception n’est levée sur 4xx/5xx. Vérifiez avec failed(), clientError(), etc.
$response = Http :: get ( 'https://api.example.com/users/999' );
if ( $response -> notFound ()) {
// 404
}
if ( $response -> failed ()) {
logger () -> error ( 'API request failed' , [ 'status' => $response -> status ()]);
}
Lever une exception
throw() lance Illuminate\Http\Client\RequestException en cas d’erreur.
// Lève sur 4xx/5xx
$response = Http :: post ( 'https://api.example.com/users' , $data ) -> throw ();
// throwIf() : si la condition est vraie
$response = Http :: get ( 'https://api.example.com/users/1' );
$response -> throwIf ( $response -> status () === 422 );
// throwUnlessStatus() : sauf pour ce code
$response = Http :: post ( 'https://api.example.com/orders' , $data );
$response -> throwUnlessStatus ( 201 );
throw() retourne la réponse — chaînable :
$user = Http :: post ( 'https://api.example.com/users' , $data )
-> throw ()
-> json ();
Gestion des exceptions :
use Illuminate\Http\Client\ RequestException ;
use Illuminate\Http\Client\ ConnectionException ;
try {
$response = Http :: timeout ( 5 )
-> post ( 'https://api.example.com/users' , $data )
-> throw ();
} catch ( ConnectionException $e ) {
// Timeout / connexion
logger () -> error ( 'Connection failed: ' . $e -> getMessage ());
} catch ( RequestException $e ) {
// 4xx / 5xx
logger () -> error ( 'API error' , [ 'status' => $e -> response -> status ()]);
}
Requêtes en parallèle
pool() exécute plusieurs requêtes en concurrence.
use Illuminate\Http\Client\ Pool ;
use Illuminate\Support\Facades\ Http ;
$responses = Http :: pool ( fn ( Pool $pool ) => [
$pool -> as ( 'users' ) -> get ( 'https://api.example.com/users' ),
$pool -> as ( 'posts' ) -> get ( 'https://api.example.com/posts' ),
$pool -> as ( 'comments' ) -> get ( 'https://api.example.com/comments' ),
]);
$users = $responses [ 'users' ] -> json ();
$posts = $responses [ 'posts' ] -> json ();
$comments = $responses [ 'comments' ] -> json ();
Bien plus rapide qu’en séquentiel. Idéal pour un dashboard qui aggrège plusieurs API.
Tests
Http::fake() pour mocker
Sans envoyer de vraies requêtes.
use Illuminate\Support\Facades\ Http ;
Http :: fake ();
// Retourne 200 partout
$response = Http :: get ( 'https://api.example.com/users' );
$response -> successful (); // true
Réponses par URL :
Http :: fake ([
'api.example.com/users/*' => Http :: response ([ 'id' => 1 , 'name' => 'Taro Yamada' ], 200 ),
'api.example.com/posts/*' => Http :: response ([ 'error' => 'Not Found' ], 404 ),
'*' => Http :: response ( 'OK' , 200 ),
]);
Séquence de réponses :
Http :: fake ([
// 1er : 200, 2e : 200, 3e : 429
// Épuisement → exception aux appels suivants
'api.example.com/*' => Http :: sequence ()
-> push ([ 'id' => 1 ], 200 )
-> push ([ 'id' => 2 ], 200 )
-> pushStatus ( 429 ),
]);
Vérification des requêtes
use Illuminate\Http\Client\ Request ;
use Illuminate\Support\Facades\ Http ;
Http :: fake ();
Http :: withToken ( 'my-token' ) -> post ( 'https://api.example.com/users' , [
'name' => 'Taro Yamada' ,
]);
Http :: assertSent ( function ( Request $request ) {
return $request -> url () === 'https://api.example.com/users'
&& $request -> hasHeader ( 'Authorization' , 'Bearer my-token' )
&& $request [ 'name' ] === 'Taro Yamada' ;
});
Http :: assertNotSent ( function ( Request $request ) {
return $request -> url () === 'https://api.example.com/admin' ;
});
Http :: assertSentCount ( 1 );
Appelez toujours Http::fake() en début de test. Sans lui, de vraies requêtes partent vers l’extérieur.
Http::preventStrayRequests() fait échouer toute requête non fakée.
Empêcher les requêtes non prévues
Http :: preventStrayRequests ();
Http :: fake ([
'api.example.com/*' => Http :: response ([ 'ok' => true ]),
]);
// Non fakée → exception
Http :: get ( 'https://other.example.com/endpoint' );
Exemple : service class pour une API externe
Bonne pratique : encapsuler la logique HTTP dans une service class.
Créer la service class
<? php
namespace App\Services ;
use Illuminate\Http\Client\ RequestException ;
use Illuminate\Http\Client\ ConnectionException ;
use Illuminate\Support\Facades\ Http ;
class GitHubService
{
private string $baseUrl = 'https://api.github.com' ;
public function __construct (
private readonly string $token ,
) {}
/**
* Récupère un utilisateur.
*
* @throws ConnectionException
* @throws RequestException
*/
public function getUser ( string $username ) : array
{
return Http :: baseUrl ( $this -> baseUrl )
-> withToken ( $this -> token )
-> acceptJson ()
-> timeout ( 10 )
-> get ( "/users/{ $username }" )
-> throw ()
-> json ();
}
/**
* Liste les dépôts.
*
* @throws ConnectionException
* @throws RequestException
*/
public function getRepositories ( string $username , int $page = 1 ) : array
{
return Http :: baseUrl ( $this -> baseUrl )
-> withToken ( $this -> token )
-> acceptJson ()
-> timeout ( 10 )
-> retry ( 2 , 500 )
-> get ( "/users/{ $username }/repos" , [
'page' => $page ,
'per_page' => 30 ,
'sort' => 'updated' ,
])
-> throw ()
-> json ();
}
}
Enregistrer dans le service provider
// app/Providers/AppServiceProvider.php
use App\Services\ GitHubService ;
public function register () : void
{
$this -> app -> singleton ( GitHubService :: class , function () {
return new GitHubService (
token : config ( 'services.github.token' ),
);
});
}
// config/services.php
'github' => [
'token' => env ( 'GITHUB_TOKEN' ),
],
Utiliser dans un contrôleur
<? php
namespace App\Http\Controllers ;
use App\Services\ GitHubService ;
use Illuminate\Http\Client\ RequestException ;
use Illuminate\Http\Client\ ConnectionException ;
use Illuminate\Http\ JsonResponse ;
class GitHubController extends Controller
{
public function __construct (
private readonly GitHubService $github ,
) {}
public function show ( string $username ) : JsonResponse
{
try {
$user = $this -> github -> getUser ( $username );
return response () -> json ( $user );
} catch ( ConnectionException ) {
return response () -> json ([ 'error' => 'Impossible de contacter l \' API GitHub.' ], 503 );
} catch ( RequestException $e ) {
$status = $e -> response -> status ();
if ( $status === 404 ) {
return response () -> json ([ 'error' => 'Utilisateur introuvable.' ], 404 );
}
return response () -> json ([ 'error' => 'Erreur de l \' API GitHub.' ], 502 );
}
}
}
Écrire un test
<? php
namespace Tests\Unit\Services ;
use App\Services\ GitHubService ;
use Illuminate\Http\Client\ ConnectionException ;
use Illuminate\Http\Client\ RequestException ;
use Illuminate\Support\Facades\ Http ;
use Tests\ TestCase ;
class GitHubServiceTest extends TestCase
{
private GitHubService $service ;
protected function setUp () : void
{
parent :: setUp ();
Http :: fake ([
'api.github.com/users/octocat' => Http :: response ([
'login' => 'octocat' ,
'name' => 'The Octocat' ,
'public_repos' => 8 ,
], 200 ),
'api.github.com/users/notfound' => Http :: response (
[ 'message' => 'Not Found' ],
404
),
]);
$this -> service = new GitHubService ( token : 'test-token' );
}
public function test_get_user () : void
{
$user = $this -> service -> getUser ( 'octocat' );
$this -> assertEquals ( 'octocat' , $user [ 'login' ]);
$this -> assertEquals ( 'The Octocat' , $user [ 'name' ]);
Http :: assertSent ( function ( $request ) {
return $request -> url () === 'https://api.github.com/users/octocat'
&& $request -> hasHeader ( 'Authorization' , 'Bearer test-token' );
});
}
public function test_not_found_throws () : void
{
$this -> expectException ( RequestException :: class );
$this -> service -> getUser ( 'notfound' );
}
}
Récapitulatif
Méthode Usage Http::get($url, $query)GET Http::post($url, $data)POST (JSON) Http::put($url, $data)PUT Http::patch($url, $data)PATCH Http::delete($url)DELETE ->withToken($token)Bearer ->withHeaders($headers)En-têtes ->timeout($seconds)Timeout ->retry($times, $sleep)Retry auto ->throw()Exception sur erreur Http::fake()Mock pour tests Http::pool($callback)Requêtes en parallèle
Méthode Description successful()2xx failed()>= 400 clientError()4xx serverError()5xx ok()200 created()201 notFound()404 unauthorized()401 forbidden()403 unprocessableEntity()422 tooManyRequests()429
Encapsulez la logique HTTP dans des service classes
Toujours définir un timeout() et un connectTimeout()
Configurez un retry() pour les erreurs transitoires
En test, Http::fake() obligatoire — ne jamais appeler l’API réelle
Ajoutez Http::preventStrayRequests() dans le setup des tests
Gérez tokens et secrets via variables d’env + config/services.php