Guida

Middleware

Usa il middleware per aspetti HTTP condivisi, collegalo a route o gruppi e tieni fuori la logica di business.

A cosa serve il middleware

In Lemonade il middleware è uno strato PSR-15 attorno all’elaborazione di una request HTTP. È adatto a condizioni sulla request, header, CSRF o controlli di accesso prima del controller. Mantieni chiaro il confine: middleware = comportamento HTTP condiviso, controller = caso d’uso HTTP concreto e orchestrazione, service = operazioni applicative o di business. Per un esempio completo di form con CSRF, continua con Form e flusso POST.

Collega il middleware a una route o a un gruppo

Usa un route middleware quando un endpoint richiede una regola HTTP aggiuntiva. Usa un group middleware quando più route condividono la stessa regola. In entrambi i casi registri le classi per nome e il runtime le risolve dal container prima del controller.

example.php
<?php

use App\Controllers\ArticleController;
use App\Controllers\ProfileController;
use App\Http\Middleware\RequirePreviewHeaderMiddleware;
use App\Http\Middleware\RequireProfileHeaderMiddleware;
use Lemonade\Framework\Routing\Router;

return static function (Router $router): void {
    $router
        ->getNamed('articles.preview', '/articles/preview', ArticleController::class . '@preview')
        ->middleware(RequirePreviewHeaderMiddleware::class);

    $router
        ->group('/profile', static function (Router $router): void {
            $router->getNamed('profile.show', '', ProfileController::class . '@show');
            $router->postNamed('profile.update', '/edit', ProfileController::class . '@update');
        })
        ->middleware(RequireProfileHeaderMiddleware::class);
};

Ordine e interruzione anticipata

I middleware vengono eseguiti nell’ordine di registrazione. L’ordine conta perché uno strato può modificare la request, aggiungere header alla response o interrompere prima la pipeline. return $handler->handle($request); passa la request allo strato successivo. Restituire una propria response termina invece quel ramo senza eseguire il resto della pipeline.

example.php
<?php

use App\Http\Middleware\RequestIdMiddleware;
use Lemonade\Framework\Core\Context\ApplicationContextFactory;
use Lemonade\Framework\Core\KernelFactory;
use Lemonade\Framework\Http\Middleware\ErrorHandlingMiddleware;
use Lemonade\Framework\Http\Middleware\MiddlewareStack;

$context = (new ApplicationContextFactory())->fromGlobals(__DIR__);
$kernel = (new KernelFactory())->create($context);

$kernel->framework()->middleware(static function (MiddlewareStack $stack): void {
    $stack->insertAfter(ErrorHandlingMiddleware::class, RequestIdMiddleware::class);
});

$kernel->handle();

Scrivi un middleware piccolo

Un middleware personalizzato implementa MiddlewareInterface e riceve le dipendenze tramite constructor injection. Mantienilo piccolo e focalizzato su HTTP. Se inizia a gestire persistenza o logica di business riutilizzabile, sposta quel lavoro in un servizio applicativo.

example.php
<?php

declare(strict_types=1);

namespace App\Http\Middleware;

use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

final class RequirePreviewHeaderMiddleware implements MiddlewareInterface
{
    public function __construct(
        private readonly ResponseFactoryInterface $responses,
    ) {}

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        if ($request->getHeaderLine('X-Preview-Mode') !== 'allow') {
            return $this->responses->createResponse(403);
        }

        return $handler->handle($request);
    }
}