Personalizzare le pagine di errore

Quando viene lanciata un’eccezione, la classe HttpKernel la cattura e distribuisce un evento kernel.exception. Questo fornisce la possibilità di convertire l’eccezione in una Response, in vari modi.

Il bundle TwigBundle ha un ascoltatore per tale evento, che eseguirà un controllore configurabile (anche se arbitrario), per generare la risposta. Il controllore predefinito ha un modo intelligente per scegliere uno dei template di errore a disposizione.

Quindi, le pagine di errore possono essere personalizzate in diversi modi, a seconda di quanto controllo si vuole avere:

  1. Usare ExceptionController predefinito e creare alcuni template, che consentono di personalizzare l’aspetto delle varie pagine di errore (facile);
  2. Sostituire il controllore predefinito con uno personalizzato (intermedio).
  3. Usare l’evento kernel.exception e implementare una gestione personalizzata (avanzato).

ExceptionController predefinito

Il metodo showAction() del controllore Symfony\Bundle\TwigBundle\Controller\ExceptionController sarà richiamato al verificarsi di un’eccezione.

Questo controllore mostrerà una pagina di eccezione o di errore, a seconda dell’impostazione di kernel.debug. Mentre le pagine di eccezione forniscono varie informazioni utili durante lo sviluppo, le pagine di errore sono rivolte all’utente finale.

Come sono scelti i template per le pagine di errore e di eccezione

Il bundle TwigBundle ha dei template predefiniti per le pagine di errore e di eccezione, nella sua cartella Resources/views/Exception.

Suggerimento

In una tipica installazione di Symfony, si può trovare TwigBundle sotto vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle. Oltre alla pagina di errore standard HTML, fornisce anche una pagina di errore per molti dei formati di risposta più comuni, inclusi JSON (error.json.twig), XML (error.xml.twig) e anche JavaScript (error.js.twig), solo per citarne alcuni.

Ecco come ExceptionController sceglierà uno dei template disponibili, in base al codice di stato HTTP e al formato della richiesta:

  • Per le pagine di errore, cerca prima un template per il formato e il codice di stato dati (come error404.json.twig);
  • Se non lo trova, cerca per un template generico per il formato dato (come error.json.twig o exception.json.twig);
  • Infine, ignora il formato e usa il template HTML (come error.html.twig o exception.html.twig).

Suggerimento

Se l’eccezione implementa l’interfaccia Symfony\Component\HttpKernel\Exception\HttpExceptionInterface, il metodo getStatusCode() sarà richiamato per ricavare il codice di stato HTTP da utilizzare. Altrimenti, il codice di stato sarà “500”.

Sovrascrivere i template degli errori

Per sovrascrivere questi template, si può semplicemente utilizzare il metodo standard per sovrascrivere i template che esistono all’interno di un bundle. Per maggiori informazioni, vedere Sovrascrivere template dei bundle.

Ad esempio, per sovrascrivere il template di errore predefinito che è mostrato all’utente finale, creare un nuovo template posizionato in app/Resources/TwigBundle/views/Exception/error.html.twig:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Si è verificato un errore: {{ status_text }}</title>
</head>
<body>
    <h1>Oops! Si è verificato un errore</h1>
    <h2>Il server ha restituito un "{{ status_code }} {{ status_text }}".</h2>
</body>
</html>

Attenzione

Non si deve usare is_granted nelle pagine di errore (o nei layout usati dalle pagine di errore), perché il router gira prima del firewall. Se il router lancia un’eccezione (per esempio, quando la rotta non esiste), l’uso di is_granted lancerà un’ulteriore eccezione. Si può usare is_granted in modo sicuro con {% if app.user and is_granted('...') %}.

Suggerimento

Non bisogna preoccuparsi, se non si ha familiarità con Twig. Twig è un semplice, potente e opzionale motore per i template che si integra con Symfony. Per maggiori informazioni su Twig, vedere Creare e usare i template.

Questa logica funziona non solo per sostituire i template predefiniti, ma anche per crearne di nuovi.

Per esempio, creare un template app/Resources/TwigBundle/views/Exception/error404.html.twig, per mostrare una pagina speciale per gli errori 404 (non trovato). Fare riferimento alla sezione precedente per l’ordine in cui ExceptionController cerca i vari nomi di template.

Suggerimento

Spesso, il modo più semplice per personalizzare una pagina di errore è quello di copiarla da TwigBundle in app/Resources/TwigBundle/views/Exception e poi modificarla.

Nota

Anche le pagine di eccezione mostrate allo sviluppatore durante il debug possono essere personalizzate, creando template come exception.html.twig, per la pagina di eccezione standard HTML, o exception.json.twig, per la pagina di eccezione JSON.

Sostituire ExceptionController

Chi avesse bisogno di un po’ più di flessibilità, oltre a riscrivere il template, può cambiare il controllore che rende la pagina di errore. Per esempio, si potrebbero voler passare variabili aggiuntive al template.

Attenzione

Assicurarsi di non perdere le pagine di eccezione che rendono gli utili messaggi di errore durante lo sviluppo.

Per farlo, basta creare un nuovo controllore e impostare l’opzione twig.exception_controller per puntarvi.

  • YAML
    # app/config/config.yml
    twig:
        exception_controller:  AppBundle:Exception:showException
    
  • XML
    <!-- app/config/config.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:twig="http://symfony.com/schema/dic/twig"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd
            http://symfony.com/schema/dic/twig
            http://symfony.com/schema/dic/twig/twig-1.0.xsd">
    
        <twig:config>
            <twig:exception-controller>AppBundle:Exception:showException</twig:exception-controller>
        </twig:config>
    </container>
    
  • PHP
    // app/config/config.php
    $container->loadFromExtension('twig', array(
        'exception_controller' => 'AppBundle:Exception:showException',
        // ...
    ));
    

Suggerimento

Si può anche impostare il controllore come servizio.

Il valore predefinito di twig.controller.exception:showAction si riferisce al metodo showAction di ExceptionController, descritto in precedenza, che è registrato nel contenitore dei servizi come twig.controller.exception.

Al controllore saranno passati due parametri: exception, che è un’istanza di \Symfony\Component\Debug\Exception\FlattenException, creata dall’eccezione gestita, e logger, un’istanza di \Symfony\Component\HttpKernel\Log\DebugLoggerInterface (che potrebbe essere null).

Suggerimento

La Request che sarà inviata al controllore è creata in Symfony\Component\HttpKernel\EventListener\ExceptionListener. Questo ascoltatore di eventi è impostato da TwigBundle.

Ovviamente, si può anche estendere Symfony\Bundle\TwigBundle\Controller\ExceptionController, come descritto prima. In tal caso, si potrebbe voler sovrascrivere uno o entrambi i metodi showAction e findTemplate. Il secondo individua il template da usare.

Attenzione

Attualmente, ExceptionController non fa parte delle API di Symfony, quindi fare attenzione: potrebbe cambiare in futuro.

Usare l’evento kernel.exception

Come menzionato in precedenza, l’evento kernel.exception viene distribuito quando il kernel di Symfony Kernel deve gestire un’eccezione. Per approfondire, si veda kernel.exception.

L’utilizzo di questo evento è in realtà molto più potente di quanto sia stato detto in precedenza, ma richiede anche una chiara comprensione del funzionamento interno di Symfony.

Per fornire solo un esempio, ipotizziamo che un’applicazione lanci eccezioni specializzate, con un significato particolare per il suo dominio.

In questo caso, tutto quello che ExceptionListener e ExceptionController possono fare è provare a immaginare il codice di stato HTTP corretto e mostrare una pagina di errore generica.

La scrittura di un ascoltatore di eventi personalizzato per l’evento kernel.exception consente di dare uno sguardo più da vicino all’eccezione e intraprendere azioni diverse. Tali azioni possono includere il log dell’eccezione, il rinvio dell’utente a un’altra pagina o la resa di pagine di errore specializzate.

Nota

Se l’ascoltatore richiama setResponse() su Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent, la propagazione dell’evento sarà stoppata e la risposta inviata al client.

Questo approccio consente di creare una gestione centralizzata e strutturata degli errori: invece di catturare (e gestire) le stesse eccezioni in vari controllori ogni volta, si può avere un solo ascoltatore (ma anche più di uno) che se ne occupi.

Suggerimento

Per un esempio, dare un’occhiata a ExceptionListener nel componente Security.

Gestisce varie eccezioni legate alla sicurezza, lanciate in un’applicazione (come Symfony\Component\Security\Core\Exception\AccessDeniedException) e mette in atto misure come il rinvio dell’utente alla pagina di login, la disconnessione e altre cose.

Buona fortuna!