# Ajout de middleware

# Introduction

Le middleware est basé sur le système défini par le routeur Slim Framework

La déclaration d'un middleware est essentiellement comme celle d'une route.

La propriété priority est obligatoire. Elle définit l'ordre dans lequel sont enregistrés les middlewares qui ont des correspondances. Attention, l'ordre d'enregistrement n'est pas forcément l'ordre d'exécution, cela dépend du code du middleware. Les ordres les plus élevés sont enregistrés en dernier.

La correspondance du pattern est identique à celui d'une route. Pour qu'un middleware soit défini pour un ensemble de routes, il est possible d'utiliser l'écriture suivante qui indique la correspondance avec toutes les urls qui commence par /my.

pattern  : "/my[/{args:.*}]"

Pour indiquer un middleware qui corresponde à toutes les routes :

methods : ["ANY"]
pattern : "[/{args:.*}]"

La propriété requiredAccess n'est pas une propriété du middleware. Le droit d'accès est uniquement pour la route, pas pour le middleware.

La méthode callable doit avoir la signature suivante :

Dans le cas, d'une méthode __invoke() : callable = \My\Routes\Test (fichier vendor/My/Routes/Test.php)

public function __invoke(
    \Psr\Http\Message\RequestInterface $request,
    \Psr\Http\Message\ResponseInterface $response,
    callable $next,
    array $args
): \Psr\Http\Message\ResponseInterface

À la différence de la route, l'argument $next est ajouté. Cet argument est la fonction à utiliser pour appeler le traitement suivant de la réponse. Le traitement suivant est soit le prochain middleware, soit la route effective s'il n'y a plus de middleware à lancer. L'argument $args contient les valeurs de la partie variable de pattern.

Exemple : Prétraitement qui ajoute le header "X-Hello: World" à la réponse.

<?php
namespace My\Middleware;

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class HeaderHelloWorld
{
  public function __invoke(Request $request, Response $response, callable $next, array $args): Response
  {
    $response = $response->withHeader("X-Hello", "World");
    $response = $next($request, $response);
    return $response;
  }
}

Exemple : Traitement qui enregistre la durée d'exécution de la route.

namespace My\Middleware;

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class LogResponse
{
    public function __invoke(Request $request, Response $response, callable $next, array $args): Response
    {
        $currentRoute = $request->getAttribute("route");
        $requestMethod = $request->getMethod();

        $mb=microtime(true);
        $response = $next($request, $response);

        error_log(sprintf("[%s]%s %s  [delay : %dms]",
            $request->getMethod() ,
            $currentRoute->getName(),
            $currentRoute->getPattern(),
            (microtime(true) - $mb) * 1000
        );

        return $response;
    }
}

# Enregistrement de middleware

L'enregistrement d'un middleware est identique à celui d'une route à la différence du mot-clef "middleware" à utiliser dans le fichier XML.

Exemple : config/MyProject/myMiddles.xml

<sde:config xmlns:router="https://platform.anakeen.com/4/schemas/sde/1.0">
  <sde:middlewares namespace="My">
        <sde:middleware name="Top">
            <sde:priority>100</sde:priority>
            <sde:callable>My\Middleware\LogResponse</sde:callable>
            <sde:method>ANY</sde:method>
            <sde:pattern>[/{args:.*}]</sde:pattern>
            <sde:description>Log duration</sde:description>
            <sde:requiredAccess/>
        </sde:middleware>
        <sde:middleware name="Header">
            <sde:priority>100</sde:priority>
            <sde:callable>My\Middleware\HeaderHelloWorld</sde:callable>
            <sde:method>GET</sde:method>
            <sde:pattern>/my/hello</sde:pattern>
            <sde:description>Add hello header</sde:description>
            <sde:requiredAccess/>
        </sde:middleware>
    </sde:middlewares>
</sde:config>