Vue d’ensemble
Dans un projet Laravel, la gestion des erreurs et exceptions est préconfigurée.
La personnalisation se fait via withExceptions dans bootstrap/app.php.
Flux de traitement d’une exception
De l’apparition d’une exception jusqu’à la réponse envoyée au client :
// bootstrap/app.php
use Illuminate\Foundation\ Application ;
use Illuminate\Foundation\Configuration\ Exceptions ;
return Application :: configure ( basePath : dirname ( __DIR__ ))
-> withExceptions ( function ( Exceptions $exceptions ) : void {
// Configuration du rapport et du rendu
}) -> create ();
L’objet $exceptions passé à la closure est une instance de Illuminate\Foundation\Configuration\Exceptions qui pilote la gestion des exceptions.
Debug
L’option debug de config/app.php contrôle le niveau d’information affiché. Par défaut, elle suit APP_DEBUG.
# Développement
APP_DEBUG =true
# Production
APP_DEBUG =false
En production, APP_DEBUG doit être false. Sinon, des informations sensibles peuvent être exposées.
Rapport d’exceptions
Rapporter, c’est journaliser une exception ou l’envoyer à Sentry , Flare , etc.
Par défaut, elles sont journalisées selon config/logging.php.
Callback de rapport personnalisé
Pour un traitement spécifique selon le type d’exception, passez une closure à report. Laravel utilise le type-hint pour identifier l’exception.
use App\Exceptions\ InvalidOrderException ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> report ( function ( InvalidOrderException $e ) {
// Notification vers un service externe, etc.
});
})
Le rapport par défaut continue. Pour l’arrêter, appelez stop() ou retournez false.
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> report ( function ( InvalidOrderException $e ) {
// ...
}) -> stop ();
})
Helper report()
Pour rapporter sans afficher de page d’erreur :
public function isValid ( string $value ) : bool
{
try {
// Validation...
} catch ( Throwable $e ) {
report ( $e );
return false ;
}
}
report() journalise sans interrompre la réponse à l’utilisateur. Utile dans les jobs en arrière-plan et pour des exceptions non critiques.
Éviter les doublons
Une même instance rapportée plusieurs fois génère des doublons.
dontReportDuplicates() limite au premier appel.
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> dontReportDuplicates ();
})
$original = new RuntimeException ( 'Whoops!' );
report ( $original ); // Journalisé
try {
throw $original ;
} catch ( Throwable $caught ) {
report ( $caught ); // Ignoré (même instance)
}
Contexte global de log
Ajoutez des informations communes à tous les logs d’exception avec context.
L’ID de l’utilisateur courant est ajouté automatiquement s’il est disponible.
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> context ( fn () => [
'app_version' => config ( 'app.version' ),
]);
})
Méthode context() sur la classe d’exception
Ajoutez du contexte spécifique à une exception.
<? php
namespace App\Exceptions ;
use Exception ;
class InvalidOrderException extends Exception
{
public function __construct (
private readonly int $orderId ,
string $message = '' ,
) {
parent :: __construct ( $message );
}
/**
* Retourne le contexte de l'exception.
*
* @return array < string , mixed>
*/
public function context () : array
{
return [ 'order_id' => $this -> orderId ];
}
}
Niveau de log
Fixez un niveau pour un type d’exception avec level.
use PDOException ;
use Psr\Log\ LogLevel ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> level ( PDOException :: class , LogLevel :: CRITICAL );
})
Limitation (throttle)
Pour un grand volume d’exceptions, limitez avec throttle.
use Illuminate\Support\ Lottery ;
use Throwable ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
// 1 chance sur 1000 de rapporter
$exceptions -> throttle ( function ( Throwable $e ) {
return Lottery :: odds ( 1 , 1000 );
});
})
Limitation par minute :
use Illuminate\Broadcasting\ BroadcastException ;
use Illuminate\Cache\RateLimiting\ Limit ;
use Throwable ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> throttle ( function ( Throwable $e ) {
if ( $e instanceof BroadcastException ) {
return Limit :: perMinute ( 300 );
}
});
})
Rendu d’exceptions
Le rendu convertit une exception en réponse HTTP. Laravel produit une réponse appropriée par défaut, mais vous pouvez la personnaliser.
Callback de rendu personnalisé
use App\Exceptions\ InvalidOrderException ;
use Illuminate\Http\ Request ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> render ( function ( InvalidOrderException $e , Request $request ) {
return response () -> view ( 'errors.invalid-order' , status : 500 );
});
})
Vous pouvez surcharger le rendu des exceptions intégrées (NotFoundHttpException…).
Si la closure ne retourne rien, le rendu par défaut est utilisé.
use Illuminate\Http\ Request ;
use Symfony\Component\HttpKernel\Exception\ NotFoundHttpException ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> render ( function ( NotFoundHttpException $e , Request $request ) {
if ( $request -> is ( 'api/*' )) {
return response () -> json ([
'message' => 'Record not found.' ,
], 404 );
}
// Retourner null → page 404 par défaut
});
})
JSON / HTML automatique
Laravel décide selon l’en-tête Accept. Pour personnaliser cette logique, utilisez shouldRenderJsonWhen.
use Illuminate\Http\ Request ;
use Throwable ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> shouldRenderJsonWhen ( function ( Request $request , Throwable $e ) {
if ( $request -> is ( 'admin/*' )) {
return true ; // Toujours JSON pour l'admin
}
return $request -> expectsJson ();
});
})
Personnaliser la réponse finale
respond transforme la réponse finale.
use Symfony\Component\HttpFoundation\ Response ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> respond ( function ( Response $response ) {
if ( $response -> getStatusCode () === 419 ) {
return back () -> with ([
'message' => 'La session a expiré. Veuillez réessayer.' ,
]);
}
return $response ;
});
})
Classes d’exception personnalisées
Créez vos exceptions dans app/Exceptions/.
Les méthodes report() et render() définies dans la classe sont appelées automatiquement, sans configuration dans bootstrap/app.php.
Créer une exception
Générer la classe
php artisan make:exception InvalidOrderException
Implémenter `report()` et `render()`
<? php
namespace App\Exceptions ;
use Exception ;
use Illuminate\Http\ Request ;
use Illuminate\Http\ Response ;
class InvalidOrderException extends Exception
{
public function __construct (
private readonly int $orderId ,
string $message = 'Invalid order.' ,
) {
parent :: __construct ( $message );
}
/**
* Rapporte l'exception.
*/
public function report () : void
{
// Notification vers un service externe...
}
/**
* Convertit l'exception en réponse HTTP.
*/
public function render ( Request $request ) : Response
{
return response () -> view ( 'errors.invalid-order' , [
'orderId' => $this -> orderId ,
], 422 );
}
}
report() accepte les type-hints pour injection. Le conteneur les résout automatiquement.
Interface ShouldntReport
Pour les exceptions à ne jamais rapporter, implémentez ShouldntReport.
<? php
namespace App\Exceptions ;
use Exception ;
use Illuminate\Contracts\Debug\ ShouldntReport ;
class PodcastProcessingException extends Exception implements ShouldntReport
{
//
}
Lever une exception
Helper abort()
Générez une réponse HTTP d’erreur depuis n’importe où.
// 404
abort ( 404 );
// Avec message
abort ( 403 , 'Vous n \' avez pas la permission d \' effectuer cette action.' );
abort_if() / abort_unless()
// abort si $condition est true
abort_if ( ! $user -> isAdmin (), 403 );
// abort si $condition est false
abort_unless ( $user -> hasPermission ( 'edit' ), 403 , 'Permission denied.' );
Utile pour les contrôles de droits dans les contrôleurs et middlewares. Souvent combiné avec Gates et Policies.
Contrôle global
Ignorer un type d’exception
dontReport ignore certaines exceptions au rapport. La logique de rendu reste active.
use App\Exceptions\ InvalidOrderException ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> dontReport ([
InvalidOrderException :: class ,
]);
})
Sous condition avec dontReportWhen :
use Throwable ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> dontReportWhen ( function ( Throwable $e ) {
return $e instanceof PodcastProcessingException &&
$e -> reason () === 'Subscription expired' ;
});
})
Laravel ignore par défaut certaines exceptions comme les erreurs 404, l’échec CSRF (419) ou l’incohérence d’origine (403).
Réactiver une exception ignorée par défaut
Utilisez stopIgnoring.
use Symfony\Component\HttpKernel\Exception\ HttpException ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> stopIgnoring ( HttpException :: class );
})
Pages d’erreur HTTP
Définissez une vue personnalisée par code HTTP.
Créer une vue d’erreur personnalisée
Placez un template Blade nommé par code dans resources/views/errors/.
resources/
└── views/
└── errors/
├── 404.blade.php
├── 403.blade.php
└── 500.blade.php
La variable $exception est disponible.
{{-- resources/views/errors/404.blade.php --}}
<! DOCTYPE html >
< html lang = "fr" >
< head >
< meta charset = "UTF-8" >
< title > Page introuvable </ title >
</ head >
< body >
< h1 > 404 - Page introuvable </ h1 >
< p > {{ $exception -> getMessage () }} </ p >
< a href = " {{ url ('/') }} " > Retour à l'accueil </ a >
</ body >
</ html >
Publier les vues par défaut
Pour partir des vues fournies par Laravel :
php artisan vendor:publish --tag=laravel-errors
Vue de repli
Créez 4xx.blade.php et 5xx.blade.php comme repli.
resources/
└── views/
└── errors/
├── 4xx.blade.php # Fallback 4xx
└── 5xx.blade.php # Fallback 5xx
Laravel fournit des pages par défaut pour 404, 500, 503. Pour les personnaliser, créez des fichiers individuels (404.blade.php…) plutôt que le fallback.
Exemple : handler d’exceptions API
Pour une application API, retournez systématiquement du JSON.
use Illuminate\Auth\ AuthenticationException ;
use Illuminate\Http\ Request ;
use Illuminate\Validation\ ValidationException ;
use Symfony\Component\HttpKernel\Exception\ NotFoundHttpException ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
// Toujours JSON pour /api
$exceptions -> render ( function ( NotFoundHttpException $e , Request $request ) {
if ( $request -> is ( 'api/*' )) {
return response () -> json ([
'message' => 'Ressource introuvable.' ,
], 404 );
}
});
$exceptions -> render ( function ( AuthenticationException $e , Request $request ) {
if ( $request -> is ( 'api/*' )) {
return response () -> json ([
'message' => 'Authentification requise.' ,
], 401 );
}
});
$exceptions -> render ( function ( ValidationException $e , Request $request ) {
if ( $request -> is ( 'api/*' )) {
return response () -> json ([
'message' => 'Erreur de validation.' ,
'errors' => $e -> errors (),
], 422 );
}
});
})
Classe d’exception API personnalisée
Une classe de base unifie les réponses.
<? php
namespace App\Exceptions ;
use Exception ;
use Illuminate\Http\ JsonResponse ;
use Illuminate\Http\ Request ;
class ApiException extends Exception
{
public function __construct (
string $message = 'An error occurred.' ,
private readonly int $statusCode = 500 ,
private readonly array $errors = [],
) {
parent :: __construct ( $message );
}
public function render ( Request $request ) : JsonResponse
{
$data = [ 'message' => $this -> getMessage ()];
if ( ! empty ( $this -> errors )) {
$data [ 'errors' ] = $this -> errors ;
}
return response () -> json ( $data , $this -> statusCode );
}
}
Exemple dans un contrôleur :
<? php
namespace App\Http\Controllers\Api ;
use App\Exceptions\ ApiException ;
use App\Models\ Order ;
class OrderController extends Controller
{
public function show ( int $id ) : JsonResponse
{
$order = Order :: find ( $id );
if ( ! $order ) {
throw new ApiException ( 'Commande introuvable.' , 404 );
}
if ( $order -> isCancelled ()) {
throw new ApiException ( 'Cette commande est annulée.' , 422 );
}
return response () -> json ( $order );
}
}
Récapitulatif
Méthode Rôle $exceptions->report()Logique de rapport par type $exceptions->context()Contexte commun à tous les logs Méthode context() Contexte propre à une exception Helper report() Rapporte sans interrompre la réponse dontReportDuplicates()Prévient les doublons Interface ShouldntReport Exception jamais rapportée
Méthode Rôle $exceptions->render()Réponse personnalisée par type Méthode render() Rendu porté par l’exception shouldRenderJsonWhen()Personnalise JSON/HTML respond()Modifie la réponse finale
Créer resources/views/errors/404.blade.php etc. suffit
$exception donne accès aux détails
php artisan vendor:publish --tag=laravel-errors publie les templates par défaut
4xx.blade.php / 5xx.blade.php pour un repli
Bonnes pratiques en production
APP_DEBUG=false obligatoire pour ne pas exposer les stack traces
Intégrer Sentry, Flare ou équivalent pour un suivi centralisé
Utiliser throttle() pour éviter de saturer les logs
Maintenir un format JSON d’erreur cohérent pour les API