Blog / Créer une page d’erreur personnalisée avec Laravel et Inertia
4 min - 12 Aug 2024
TL;DR: Comment afficher une page d’erreur personnalisée avec Inertia au lieu des erreurs par défaut de Laravel.
Vous trouverez un projet Laravel d’exemple sur ce Github Repository. Découvrez en plus sur Capsules ou X.
Dans un article précédent, le quatrième article de ce blog. il était question de personnaliser la page d’erreur 502 Bad Gateway servie par Nginx. Celle-ci était une page HTML placée directement dans le dossier /public
. Cet article se concentre désormais sur la personnalisation des pages d’erreur retournées par Laravel dans un projet VILT.
Pour avoir un aperçu du layout des pages d’erreur Laravel, il suffit de générer une erreur en utilisant la Facade App::abort
ou le helper abort
, suivi du code d’erreur désiré.
routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\App;
Route::get( '/', fn() => App::abort( 503 ) );
Route::get( '/', fn() => App::abort( 419 ) );
Route::get( '/', fn() => App::abort( 404 ) );
En approfondissant le fonctionnement de la fonction abort
, force est de constater que Laravel génère une Exception
, laquelle est ensuite traitée par le ExceptionHandler
créé par la méthode withExceptions()
dans le fichier bootstrap/app.php
.
vendor/laravel/framework/src/Illuminate/Foundation/Application.php
public function abort($code, $message = '', array $headers = [])
{
if ($code == 404) {
throw new NotFoundHttpException($message, null, 0, $headers);
}
throw new HttpException($code, $message, null, $headers);
}
vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php
protected function prepareResponse($request, Throwable $e)
{
if (! $this->isHttpException($e) && config('app.debug')) {
return $this->toIlluminateResponse($this->convertExceptionToResponse($e), $e)->prepare($request);
}
if (! $this->isHttpException($e)) {
$e = new HttpException(500, $e->getMessage(), $e);
}
return $this->toIlluminateResponse(
$this->renderHttpException($e), $e
)->prepare($request);
}
bootstrap/app.php
<?php
use Illuminate\Foundation\Application;
return Application::configure( basePath : dirname( __DIR__ ) )
->withRouting( web : base_path( "/routes/web.php" ) )
->withExceptions()
->create();
withExceptions
est retirée du fichier, plus rien ne fonctionne. Il est donc crucial de ne pas négliger cette méthode.Pour permettre à Inertia d’intercepter à son tour les différentes exceptions, il est nécessaire de modifier le fichier bootstrap/app.php
. C’est principalement dans ce fichier que la magie opère.
Et plus particulièrement au niveau de la méthode withExceptions
: l’interception des exceptions. Lorsque l’exception est un HttpException
, ce qui est l’objet de cet article, le rendu Inertia est déclenché. La méthode $exceptions->render( fn( HttpException $exception, ... )
permet de filtrer les types d’exception. Si l'exception est une HttpException
, le processus de rendu Inertia se poursuit. Inertia retourne alors la page Error
avec la réponse $request
et son code de status.
bootstrap/app.php
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Http\Request;
use Inertia\Inertia;
return Application::configure( basePath : dirname( __DIR__ ) )
->withRouting( web : base_path( "/routes/web.php" ) )
->withExceptions( fn( Exceptions $exceptions ) =>
$exceptions->render( fn( HttpException $exception, Request $request ) =>
Inertia::render( 'Error', [ 'status' => $exception->getStatusCode() ] )
->toResponse( $request )
->setStatusCode( $exception->getStatusCode() )
)
)
->create();
Il est alors possible de personnaliser les pages d'erreur souhaitées en utilisant un composant Vue Error
dédié aux erreurs.
resources/js/pages/Error.vue
<script setup>
import { Head } from '@inertiajs/vue3'
import { useStatusName, useStatusMessage } from '~/composables/status';
import logotype from '/public/assets/capsules-logotype.svg';
const props = defineProps( { status : Number } );
</script>
<template>
<Head>
<title>{{ useStatusName( status ) }}</title>
</Head>
<div class="h-screen flex justify-center">
<div class="flex items-center space-x-10">
<img class="h-20 w-20" v-bind:src="logotype">
<div class="flex flex-col items-start space-y-2 font-mono text-primary-black">
<h1 class="text-2xl text-primary-blue" v-text="`${props.status} - ${useStatusName( status )}`" />
<p class="text-md" v-text="useStatusMessage( status )" />
</div>
</div>
</div>
</template>
Head
permet de personnaliser la section <head>
de la page HTML. Il ne faut cependant pas oublier d’inclure @inertiaHead
dans le fichier app.blade.php
Ainsi qu’un composable status
qui retourne les codes et messages d’erreur recherchés.
resources/js/composables/status.js
export function useStatusName( status )
{
switch( status )
{
case 503 : return 'Service Unavailable';
case 500 : return 'Server Error';
case 419 : return 'Page Expired';
case 418 : return 'I\'m a teapot';
case 404 : return 'Not Found';
case 403 : return 'Forbidden';
default : return 'Error';
}
}
export function useStatusMessage( status )
{
switch( status )
{
case 503 : return 'Sorry, we are doing some maintenance. Please check back soon.';
case 500 : return 'Whoops, something went wrong on our servers.';
case 419 : return 'Sorry, your page expired.';
case 418 : return 'Sorry, I am not a coffee machine.';
case 404 : return 'Sorry, the page you are looking for could not be found.';
case 403 : return 'Sorry, you are forbidden from accessing this page.';
default : return 'Sorry, something went wrong unexpectedly.';
}
}
Pour tester les différentes exceptions directement depuis le navigateur :
routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\App;
Route::get( '/{status?}', fn( int | null $status = null ) => App::abort( $status ?? 418 ) );
herd link article
npm run dev
> http://article.test/503
> http://article.test/419
> http://article.test/404
Ravi d’avoir pu aider !