Chapitre 2.7 : Gestion des erreurs
Temps d'étude : 40 minutes
1. Pourquoi les erreurs standards sont-elles mauvaises ?
Si une erreur se produit dans votre application Laravel (par exemple, un enregistrement introuvable dans la base de données) et que vous ne l'avez pas gérée, l'utilisateur verra une énorme page HTML avec des informations de débogage ou un message non informatif "Server Error".
Pour une API, c'est une catastrophe. Votre application frontend s'attend à recevoir du JSON, pas du HTML. Notre tâche est d'intercepter toute erreur et de la transformer en une réponse JSON structurée.
2. Dispatcher d'erreurs central : bootstrap/app.php
Dans les anciennes versions de Laravel, il y avait un fichier volumineux App\Exceptions\Handler.php
. Dans Laravel 11/12, tout est devenu beaucoup plus simple et élégant. Le centre de gestion des erreurs se trouve désormais directement dans le fichier de configuration de votre application — bootstrap/app.php
.
Ouvrez bootstrap/app.php
. Tout en bas, vous verrez le bloc .withExceptions(...)
. C'est notre "dispatcher central".
<?php
// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
// ...
})
->withExceptions(function (Exceptions $exceptions) {
// <-- C'EST ICI QUE NOUS ALLONS TRAVAILLER
})->create();
3. Gérer l'erreur la plus courante : "Non trouvé" (404)
L'erreur la plus courante dans une API est lorsque l'utilisateur demande une ressource qui n'existe pas (par exemple, GET /api/planets/999
). Dans ce cas, Laravel génère une exception ModelNotFoundException
ou NotFoundHttpException
. Interceptons-les.
Ajoutez le code suivant à l'intérieur de .withExceptions(...)
:
<?php
// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
// Intercepte l'exception lorsque le modèle n'est pas trouvé dans la base de données
$exceptions->render(function (ModelNotFoundException $e, Request $request) {
// Vérifie que la requête provient bien de notre API
if ($request->is('api/*')) {
return response()->json([
'message' => 'La ressource demandée est introuvable dans notre galaxie.'
], 404);
}
});
// Intercepte l'exception lorsque le chemin lui-même n'est pas trouvé
$exceptions->render(function (NotFoundHttpException $e, Request $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => 'Une telle route spatiale n\'existe pas.'
], 404);
}
});
})->create();
$exceptions->render(...)
— nous enregistrons un "gestionnaire". Il dit : "Si une exception de typeModelNotFoundException
se produit, exécute ce code".if ($request->is('api/*'))
— c'est une vérification importante. Elle garantit que notre belle réponse JSON ne sera envoyée que pour les requêtes API, sans affecter les pages web normales.return response()->json(...)
— nous créons et renvoyons une réponse JSON standardisée avec le code 404.
Maintenant, si vous demandez une planète inexistante, au lieu d'une page HTML disgracieuse, vous obtiendrez un JSON propre.
4. Exceptions personnalisées : Créer nos propres "alertes"
Parfois, les exceptions standards ne suffisent pas. Imaginons que nous ayons une règle métier : "il est interdit de supprimer la planète 'Terre'". Si quelqu'un tente de le faire, nous devons renvoyer une erreur significative.
Étape 1 : Créer notre classe d'exception Exécuter dans le terminal :
Étape 2 : L'utiliser dans le contrôleur
Ouvrez PlanetController.php
et modifiez la méthode destroy
:
<?php
// app/Http/Controllers/PlanetController.php
use App\Exceptions\CannotDeleteEarthException; // <-- Importons notre exception
use App\Models\Planet;
public function destroy(Planet $planet)
{
// Notre nouvelle règle métier
if (strtolower($planet->name) === 'земля') {
throw new CannotDeleteEarthException('La suppression de la planète Terre est interdite par le Code Galactique.');
}
$planet->delete();
return response()->json(null, 204);
}
DELETE /api/planets/1
(où 1 est l'ID de la Terre), notre code lèvera une exception CannotDeleteEarthException
.
Étape 3 : Apprendre à Laravel à gérer joliment notre "alerte"
Revenons à bootstrap/app.php
et ajoutons un nouveau gestionnaire pour notre exception.
<?php
// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
// Notre nouveau gestionnaire
$exceptions->render(function (CannotDeleteEarthException $e, Request $request) {
return response()->json([
'message' => 'Opération interdite.',
'details' => $e->getMessage() // Récupère le message que nous avons passé à throw
], 403); // 403 Forbidden - "Accès interdit"
});
// ... (autres gestionnaires pour 404)
})->create();
5. Gestion de tous les autres échecs (500 Internal Server Error)
Que faire de toutes les autres erreurs imprévues ? Par exemple, si la base de données tombe en panne ou s'il y a une erreur de syntaxe dans le code. Pour cela, nous pouvons enregistrer un gestionnaire "universel" pour le type d'erreur le plus général — Throwable
.
Important : Ce gestionnaire doit être le dernier pour ne pas intercepter les exceptions plus spécifiques que nous avons définies ci-dessus.
<?php
// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
// ... (gestionnaires pour CannotDeleteEarthException et 404)
// GESTIONNAIRE UNIVERSEL (à la toute fin)
$exceptions->render(function (Throwable $e, Request $request) {
if ($request->is('api/*')) {
// En mode débogage, il est possible d'afficher le véritable message d'erreur
$message = config('app.debug')
? 'Une erreur est survenue : ' . $e->getMessage()
: 'Une erreur inattendue est survenue à bord. Les ingénieurs ont déjà été appelés.';
return response()->json(['message' => $message], 500);
}
});
})->create();
Maintenant, toute exception "inconnue" sera proprement interceptée et transformée en JSON avec le code 500, sans casser votre API et sans montrer d'informations superflues à l'utilisateur.
6. Journalisation des erreurs : La boîte noire du vaisseau spatial
Paramètres de journalisation dans config/logging.php
:
<?php
'channels' => [
'space_api' => [
'driver' => 'daily',
'path' => storage_path('logs/space_api.log'),
'level' => 'error',
'days' => 14,
],
],
Ajouter une entrée au journal :
<?php
try {
// Code avec risque d'erreur
} catch (Exception $e) {
Log::channel('space_api')->error('Erreur d\'accès aux planètes', [
'exception' => $e,
'request' => request()->all(),
'user_id' => auth()->id()
]);
throw $e;
}
Quiz de révision
🚀 Résumé du chapitre :
Vous avez équipé votre API d'un système de sauvetage fiable : - 🛟 Interception globale des erreurs standard - 🪐 Exceptions personnalisées avec des codes clairs - 📝 Format JSON unifié pour toutes les erreurs - 🔍 Journalisation avec les détails de l'incident - 📡 Intégration avec les systèmes de surveillance
Le vaisseau spatial est prêt aux situations d'urgence ! Dans le chapitre final de cette section, nous allons tester tous les systèmes.
📌 Vérification :
- Créez une exception
PlanetNotFoundException
- Ajoutez la gestion des erreurs 404 dans
->withExceptions
- Testez une requête vers une planète inexistante
⚠️ Si les erreurs ne sont pas interceptées :
- Assurez-vous que
is('api/*')
correspond à vos routes- Vérifiez l'ordre des gestionnaires dans
register()
- Pour les exceptions personnalisées, utilisez
throw new