backend / php-laravel
Middleware
Explication
Ce que vous allez apprendre
- Écrire un middleware qui agit avant l'exécution du contrôleur
- Écrire un middleware qui traite la réponse après que le contrôleur a répondu
- Passer un paramètre à un middleware directement depuis une route
- Enregistrer un alias de middleware et l'appliquer sur des groupes de routes
- Comprendre pourquoi l'ordre des middlewares dans un groupe a une importance réelle
Dans quel contexte ?
Une application doit vérifier que chaque visiteur d'un espace "premium" a bien un abonnement actif, mesurer le temps de réponse de chaque requête pour détecter les lenteurs, et restreindre certaines pages aux seuls administrateurs. Plutôt que de dupliquer ces vérifications dans chaque contrôleur concerné, un middleware centralise chacune de ces règles transversales à un seul endroit, appliqué automatiquement aux bonnes routes.
D'abord, un middleware s'intercale dans le trajet d'une requête
Un middleware Laravel reçoit la requête et une fonction $next. Tout ce qui est écrit avant l'appel à $next($request) s'exécute AVANT que le contrôleur ne soit atteint ; tout ce qui suit cet appel s'exécute APRÈS que le reste de la chaîne (y compris le contrôleur) a terminé.
VerifierAbonnementActif illustre le premier cas : si la condition échoue, il renvoie directement une réponse sans jamais appeler $next(), ce qui court-circuite complètement l'exécution du contrôleur.
Prérequis
Il faut être à l'aise avec le routing et les groupes de routes (leçon 8) : les middlewares s'appliquent le plus souvent sur des groupes entiers de routes.
Ensuite, un middleware peut aussi agir après coup
LoggerTempsReponse illustre le second cas : il capture un timestamp de départ, laisse $next($request) s'exécuter entièrement (contrôleur compris), puis mesure et ajoute un en-tête personnalisé à la réponse déjà construite. C'est le pattern à utiliser pour tout ce qui doit observer ou modifier une réponse déjà produite.
| Moment d'action | Exemple | Structure du code |
|---|---|---|
Avant $next() | Vérifier l'authentification | if (...) return response(...); return $next($request); |
Après $next() | Ajouter un en-tête, logger la durée | $response = $next($request); ...; return $response; |
Il reste à rendre un middleware configurable
Un middleware peut recevoir des paramètres supplémentaires directement depuis la route, comme ->middleware('role:admin'). Le paramètre $role arrive alors en troisième argument de la méthode handle(), après $request et $next — ce qui permet de réutiliser le même middleware VerifierRole pour plusieurs rôles différents sans dupliquer de code.
Piège fréquent
Placer role:admin AVANT auth dans la liste des middlewares d'une route provoquerait une erreur, puisque VerifierRole a besoin que $request->user() soit déjà résolu par le middleware d'authentification. L'ordre des middlewares dans un tableau ou un groupe n'est jamais arbitraire : il reflète des dépendances réelles entre eux.
Maintenant, où et comment enregistrer un middleware
Depuis Laravel 11, l'enregistrement se fait de façon centralisée dans bootstrap/app.php, via withMiddleware(). Les alias ($middleware->alias(['role' => VerifierRole::class])) permettent d'utiliser un nom court dans les routes plutôt que le nom de classe complet.
Astuce
Un middleware global ajouté via $middleware->web(append: [...]) s'applique automatiquement à TOUTES les requêtes web de l'application, sans qu'il faille le déclarer sur chaque route individuellement. Réserve cette approche aux comportements vraiment transversaux, comme le logging de performance.
Maintenant que tu sais intercepter une requête pour vérifier des conditions transversales, la prochaine leçon aborde une application directe et fondamentale de ce principe : l'authentification des utilisateurs avec Breeze et Sanctum.
Commandes & code
Middleware
<?php
// app/Http/Middleware/VerifierAbonnementActif.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class VerifierAbonnementActif {
// handle() s'exécute AVANT le contrôleur ; $next() poursuit la chaîne
public function handle(Request $request, Closure $next): Response {
if (!$request->user()?->abonnementActif()) {
return response()->json(['erreur' => 'Abonnement requis'], 402);
}
return $next($request);
}
}
// Middleware avec logique APRÈS la réponse (post-traitement)
class LoggerTempsReponse {
public function handle(Request $request, Closure $next): Response {
$debut = microtime(true);
$response = $next($request); // exécute le reste de la chaîne + le contrôleur
$duree = round((microtime(true) - $debut) * 1000, 2);
$response->headers->set('X-Temps-Reponse', "{$duree}ms");
if ($duree > 1000) {
\Log::warning("Requête lente: {$request->path()} ({$duree}ms)");
}
return $response;
}
}
// Middleware avec paramètres
class VerifierRole {
public function handle(Request $request, Closure $next, string $role): Response {
if (!$request->user()?->hasRole($role)) {
abort(403, "Rôle '$role' requis");
}
return $next($request);
}
}<?php
// bootstrap/app.php (Laravel 11+) : enregistrement des middlewares
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withMiddleware(function (Middleware $middleware) {
// Alias utilisable dans les routes : ->middleware('role:admin')
$middleware->alias([
'role' => \App\Http\Middleware\VerifierRole::class,
'abonnement' => \App\Http\Middleware\VerifierAbonnementActif::class,
]);
// Middleware global appliqué à toutes les requêtes web
$middleware->web(append: [\App\Http\Middleware\LoggerTempsReponse::class]);
})
->create();<?php
// Utilisation sur des routes, avec paramètre
Route::get('/admin/rapports', [RapportController::class, 'index'])
->middleware(['auth', 'role:admin']);
Route::middleware(['auth', 'abonnement'])->group(function () {
Route::get('/premium/contenu', [ContenuController::class, 'index']);
});Résumé
- Un middleware peut agir avant (
ifpuis$next()) ou après (traiter$responseretourné par$next()) la requête. - Les alias (
role:admin) permettent de paramétrer un middleware directement dans les routes. - L'ordre des middlewares dans un groupe est important :
authdoit précéder les vérifications de rôle. - Un middleware global (
$middleware->web()) s'applique à toutes les requêtes du groupe concerné.
Exercices pratiques
Mission : reparer un middleware de role mal ordonne
Objectif : Corriger un ordre de middleware incorrect et ajouter un middleware de mesure de temps de reponse pose apres le traitement.
Contexte
Une route /admin/rapports declare ->middleware(['role:admin', 'auth']), dans cet ordre precis. Depuis ce matin, tous les utilisateurs (meme les administrateurs legitimes deja connectes) recoivent une erreur 500 en visitant cette page, avec un message d'erreur mentionnant un appel de methode sur null. Par ailleurs, l'equipe veut mesurer le temps de reponse de chaque requete admin et l'ajouter en en-tete HTTP, sans jamais bloquer la requete elle-meme.