C’est quoi un middleware ?

Pour faire simple, ce sont des fonctions qui permettent d’enrichir ou de vérifier une requête avant qu’elle arrive à votre controller.

Plusieurs middlewares sont fournis de base avec Laravel comme auth pour autoriser l’accès à la route seulement aux requêtes liées à un utilisateur connecté.

Middlewares par défaut

Laravel inclut les middlewares suivant dans chaque nouveau projet Laravel :

  • Authenticate : renvoie une 401 si la requête ne contient pas d’Authorization
  • CheckForMaintenanceMode : Si l’application est en maintenance, vérifie si l’IP ou la requête est autorisée pour l’exécuter malgré la maintenance
  • EncryptCookies : Encrypte les cookies
  • RedirectIfAuthenticated : Redirige sur la route définie comme Home si la requête est un utilisateur connecté
  • TrimStrings : Trim chaque champ du body de la requête
  • TrustProxies : Liste des proxies qui envoie les requêtes à votre server
  • VerifyCsrfToken : Vérifie le token Csrf fournit par la requête

Le framework lui même en contient de nombreux autres :

  • AuthenticateWithBasicAuth
  • Authorize
  • EnsureEmailsVerified
  • RequiredPassword
  • AddQueuedCookiesToResponse
  • ConvertEmptyStringToNull
  • TransformRequest
  • ValidatePostSize
  • CheckResponseForModifications
  • FrameGuard
  • SetCacheHeadersd
  • SubstituteBindings
  • ThrottleRequests
  • ThrottleRequestsWithRedis
  • ValidateSignature
  • AuthenticateSession
  • StartSession
  • ShareErrorsFromSession

Créer votre middleware

Avec artisan, il est possible de créer votre propre middleware avec la commande :

php artisan make:middleware CheckAge

Pour Lumen, il est possible de créer directement le fichier dans app/Http/Middleware.

 <?php
namespace App\Http\Middleware;
use Closure;
class CheckAge { 
	/** 
    * Handle an incoming request. 
    * 
    * @param \Illuminate\Http\Request $request 
    * @param \Closure $next * @return mixed 
    */ 
    public function handle($request, Closure $next)
    {
    	if ($request->age <= 200) {
    		return redirect(‘home’); 
        }
	}
}

Cette classe dispose d’une fonction handle qui sera appelé par le framework quand vous allez utiliser votre middleware. Le plus important est l’appel au callback next qui permet de continuer la requête.

Il est possible d’ajouter des paramètres à la fonction handle que vous pourrez définir quand vous l’appelerez.

<?php
namespace App\Http\Middleware;
use Closure;
class CheckRole { 
	/**
    * Handle the incoming request. 
    * 
    * @param \Illuminate\Http\Request $request 
    * @param \Closure $next 
    * @param string $role 
    * @return mixed 
    */ 
    public function handle($request, Closure $next, $role)
    {
    	if (! $request->user()->hasRole($role)) {
        // Redirect… 
        }
	}
}

Une fois votre middleware créé, il ne nous reste plus qu’à l’enregistrer pour qu’il soit reconnu par Laravel.

Enregistrer votre middleware

En fonction de votre besoin, il existe plusieurs manières d’enregistrer votre middleware : - pour toute votre application - pour une seule route - dans un groupe de middleware

Enregistrer un middleware pour toute votre application

Pour qu’il se déclenche à chaque requête, il faut l’enregistrer dans l’attribut $middleware dans le fichier app/Http/Kernel.php.

Enregistrer un middleware pour une seule route

La liste des middlewares utilisable pour vos routes ou groupes de route se trouve dans l’attribut $routeMiddleware de la classe App\Http\Kernel. Elle prend la forme d’un tableau associatif où la clef est une chaîne qui va servir d’identifiant et la valeur est la classe de votre middleware.

Ensuite on peut l’utiliser directement lorsqu’on enregistre nos routes.

Route::get('admin/profile', "MonController@fonction")->middleware('auth');

Il est possible d’utiliser le nom de la classe plutôt que la clé du tableau pour bénéficier de l’autocompletion.

Route::get('admin/profile', "MonController@fonction")->middleware(CheckAge::class);

Il est également possible d’enregistrer plusieurs middlewares pour une même route.

Route::get('/', function () {})->middleware('first', 'second');

Enfin, il est possible d’empêcher l’exécution d’un middleware d’un groupe pour une de ses routes.

Route::middleware([CheckAge::class])->group(function () {
	Route::get(‘/’, function () {});
	Route::get('admin/profile', function () {})->withoutMiddleware([CheckAge::class]);
});

Enregistrer un middleware pour un groupe de requêtes

Dans le cas de middlewares que vous allez utiliser ensemble, il est possible de créer un groupe de middlewares dans la propriété $middlewareGroups. Il vous suffira ensuite d’appeler uniquement ce groupe pour appliquer tous ses middlewares.

C’est ce que fait Laravel par défaut avec les groupes web et api.

/**
* The application's route middleware groups.
* @var array
*/
protected $middlewareGroups = [
	'web' => [
		\App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
	'api' => [
    	'throttle:60,1','auth:api',
    ],
];

Passer des arguments à votre middleware

L’ensemble des arguments se passe à votre middleware avec un : après l’identifiant du middleware. S’il y a plusieurs arguments, on les sépare par des ,.

Route::put('post/{id}', function ($id) {})->middleware('role:editor');