Panoramica
Quando crei un progetto Laravel, la gestione di errori ed eccezioni è già preconfigurata.
Personalizzi con il metodo withExceptions in bootstrap/app.php.
Flusso di gestione delle eccezioni
Dal momento in cui viene sollevata un’eccezione fino alla risposta al client.
// bootstrap/app.php
use Illuminate\Foundation\ Application ;
use Illuminate\Foundation\Configuration\ Exceptions ;
return Application :: configure ( basePath : dirname ( __DIR__ ))
-> withExceptions ( function ( Exceptions $exceptions ) : void {
// Configura qui report e rendering delle eccezioni
}) -> create ();
L’oggetto $exceptions è un’istanza di Illuminate\Foundation\Configuration\Exceptions che gestisce l’handler dell’applicazione.
Debug
L’opzione debug di config/app.php controlla quanto viene mostrato.
Di default segue APP_DEBUG in .env.
# Locale
APP_DEBUG =true
# Produzione
APP_DEBUG =false
In produzione imposta sempre APP_DEBUG a false. Lasciarlo su true espone dati sensibili agli utenti finali.
Reporting delle eccezioni
Il reporting è la registrazione delle eccezioni in log o su servizi esterni come Sentry o Flare .
Di default vanno nel log secondo config/logging.php.
Callback di report personalizzate
Per gestire in modo diverso i vari tipi di eccezione, passa una closure a report. Laravel deduce il tipo dal type hint.
use App\Exceptions\ InvalidOrderException ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> report ( function ( InvalidOrderException $e ) {
// Notifica a servizi esterni, ecc.
});
})
Anche con callback personalizzate, il log di default continua. Per fermare la propagazione chiama stop() o restituisci false.
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> report ( function ( InvalidOrderException $e ) {
// ...
}) -> stop ();
})
Helper report()
Per segnalare un’eccezione senza mostrare una pagina d’errore, usa report().
public function isValid ( string $value ) : bool
{
try {
// Logica di validazione...
} catch ( Throwable $e ) {
report ( $e );
return false ;
}
}
report() registra l’errore senza interrompere la risposta all’utente. Utile per background job e operazioni non critiche.
Prevenire duplicati
Se la stessa istanza viene passata più volte a report() compaiono log duplicati. Con dontReportDuplicates() viene registrata solo la prima volta.
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> dontReportDuplicates ();
})
$original = new RuntimeException ( 'Whoops!' );
report ( $original ); // Registrato
try {
throw $original ;
} catch ( Throwable $caught ) {
report ( $caught ); // Ignorato (stessa istanza)
}
Contesto globale del log
Per aggiungere informazioni comuni a tutti i log di eccezioni usa context.
L’ID utente corrente viene aggiunto automaticamente se disponibile.
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> context ( fn () => [
'app_version' => config ( 'app.version' ),
]);
})
Metodo context() sulla classe eccezione
Definendo context() sulla classe eccezione includi informazioni specifiche di quella eccezione.
<? php
namespace App\Exceptions ;
use Exception ;
class InvalidOrderException extends Exception
{
public function __construct (
private readonly int $orderId ,
string $message = '' ,
) {
parent :: __construct ( $message );
}
/**
* @return array < string , mixed>
*/
public function context () : array
{
return [ 'order_id' => $this -> orderId ];
}
}
Livello di log
Per registrare certe eccezioni a un livello specifico usa level.
use PDOException ;
use Psr\Log\ LogLevel ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> level ( PDOException :: class , LogLevel :: CRITICAL );
})
Throttling delle segnalazioni
In caso di molte eccezioni puoi limitare le segnalazioni con throttle.
use Illuminate\Support\ Lottery ;
use Throwable ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
// 1 segnalazione ogni 1000 casuali
$exceptions -> throttle ( function ( Throwable $e ) {
return Lottery :: odds ( 1 , 1000 );
});
})
Per limite al minuto usa Limit.
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 );
}
});
})
Rendering delle eccezioni
Il rendering trasforma l’eccezione in una risposta HTTP. Laravel lo fa in automatico, ma puoi personalizzare.
Callback di rendering personalizzate
Con una closure passata a render trasformi l’eccezione in risposta.
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 );
});
})
Puoi sovrascrivere anche il rendering di eccezioni integrate (come NotFoundHttpException).
Se la closure non ritorna nulla, viene usato il rendering di default.
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 );
}
// null → pagina 404 di default
});
})
JSON o HTML in automatico
Laravel decide in base all’header Accept. Per personalizzare la logica usa 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 ;
}
return $request -> expectsJson ();
});
})
Manipolare la risposta finale
Con respond modifichi la risposta generata.
use Symfony\Component\HttpFoundation\ Response ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> respond ( function ( Response $response ) {
if ( $response -> getStatusCode () === 419 ) {
return back () -> with ([
'message' => 'La pagina è scaduta. Riprova.' ,
]);
}
return $response ;
});
})
Classi eccezione personalizzate
Puoi creare eccezioni personalizzate in app/Exceptions/.
Se definisci report() e render() vengono chiamati automaticamente senza dover configurare bootstrap/app.php.
Creare la classe eccezione
Crea la classe
php artisan make:exception InvalidOrderException
Implementa report() e 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 );
}
public function report () : void
{
// Notifica a servizi esterni
}
public function render ( Request $request ) : Response
{
return response () -> view ( 'errors.invalid-order' , [
'orderId' => $this -> orderId ,
], 422 );
}
}
In report() puoi usare la dependency injection tramite type hint. Il service container risolve automaticamente.
Interfaccia ShouldntReport
Per eccezioni che non vanno mai segnalate implementa ShouldntReport.
<? php
namespace App\Exceptions ;
use Exception ;
use Illuminate\Contracts\Debug\ ShouldntReport ;
class PodcastProcessingException extends Exception implements ShouldntReport
{
//
}
Lanciare eccezioni
Helper abort()
Ovunque nell’applicazione puoi generare una risposta di errore HTTP.
// 404
abort ( 404 );
// Con messaggio
abort ( 403 , 'Non hai i permessi per questa operazione.' );
abort_if() / abort_unless()
Helper per lanciare eccezioni condizionali.
// Abort se la condizione è vera
abort_if ( ! $user -> isAdmin (), 403 );
// Abort se la condizione è falsa
abort_unless ( $user -> hasPermission ( 'edit' ), 403 , 'Permesso negato.' );
Comodi per i controlli di permesso in controller e middleware, spesso combinati con gate e policy.
Controllo globale delle eccezioni
Ignorare eccezioni
Escludi eccezioni dal reporting con dontReport. La logica di rendering personalizzata continua a funzionare.
use App\Exceptions\ InvalidOrderException ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> dontReport ([
InvalidOrderException :: class ,
]);
})
Condizionalmente con dontReportWhen.
use Throwable ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> dontReportWhen ( function ( Throwable $e ) {
return $e instanceof PodcastProcessingException &&
$e -> reason () === 'Subscription expired' ;
});
})
Laravel ignora già di default alcune eccezioni (404, CSRF 419, mismatch origin 403, ecc.).
Riportare eccezioni ignorate
Con stopIgnoring riporti eccezioni ignorate.
use Symfony\Component\HttpKernel\Exception\ HttpException ;
-> withExceptions ( function ( Exceptions $exceptions ) : void {
$exceptions -> stopIgnoring ( HttpException :: class );
})
Pagine di errore HTTP
Puoi definire viste personalizzate per ogni codice HTTP.
Creare viste di errore
Crea in resources/views/errors/ template Blade con il codice HTTP come nome.
resources/
└── views/
└── errors/
├── 404.blade.php
├── 403.blade.php
└── 500.blade.php
Nella vista usa $exception.
{{-- resources/views/errors/404.blade.php --}}
<! DOCTYPE html >
< html lang = "it" >
< head >
< meta charset = "UTF-8" >
< title > Pagina non trovata </ title >
</ head >
< body >
< h1 > 404 - Pagina non trovata </ h1 >
< p > {{ $exception -> getMessage () }} </ p >
< a href = " {{ url ('/') }} " > Torna alla home </ a >
</ body >
</ html >
Pubblicare i template di default
Per personalizzarli usa vendor:publish.
php artisan vendor:publish --tag=laravel-errors
Pagine di fallback
Crea 4xx.blade.php e 5xx.blade.php per fare da fallback.
resources/
└── views/
└── errors/
├── 4xx.blade.php
└── 5xx.blade.php
Per 404, 500, 503 Laravel ha pagine di default. Per personalizzarli crea i file specifici, non il fallback.
Esempio pratico: handler API
Nelle applicazioni API le eccezioni vanno restituite sempre in JSON. Esempio in bootstrap/app.php.
use Illuminate\Auth\ AuthenticationException ;
use Illuminate\Http\ Request ;
use Illuminate\Validation\ ValidationException ;
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' => 'Risorsa non trovata.' ,
], 404 );
}
});
$exceptions -> render ( function ( AuthenticationException $e , Request $request ) {
if ( $request -> is ( 'api/*' )) {
return response () -> json ([
'message' => 'Autenticazione richiesta.' ,
], 401 );
}
});
$exceptions -> render ( function ( ValidationException $e , Request $request ) {
if ( $request -> is ( 'api/*' )) {
return response () -> json ([
'message' => 'Errore di validazione.' ,
'errors' => $e -> errors (),
], 422 );
}
});
})
Eccezione API personalizzata
<? 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 );
}
}
Uso nel controller:
<? 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 ( 'Ordine non trovato.' , 404 );
}
if ( $order -> isCancelled ()) {
throw new ApiException ( 'Ordine già annullato.' , 422 );
}
return response () -> json ( $order );
}
}
Riepilogo
Modalità Uso $exceptions->report()Callback per tipo di eccezione $exceptions->context()Info comuni a tutti i log Metodo context() Info specifiche della classe eccezione Helper report() Segnala senza interrompere dontReportDuplicates()Evita duplicati ShouldntReportClassi mai segnalate
Rendering delle eccezioni
Modalità Uso $exceptions->render()Response per tipo di eccezione Metodo render() Response definita nella classe shouldRenderJsonWhen()Personalizza JSON/HTML respond()Manipola la response
Crea resources/views/errors/404.blade.php ecc. per attivarle
Usa $exception per i dettagli
vendor:publish --tag=laravel-errors per i template di default
4xx.blade.php / 5xx.blade.php come fallback
Best practice in produzione
APP_DEBUG=false sempre, per non esporre lo stack trace
Integra Sentry, Flare o strumenti simili
throttle() per non intasare i log
Mantieni un formato JSON coerente per gli errori delle API