Guía

Middleware

Usa middleware para reglas HTTP compartidas, asígnalo a rutas o grupos y mantén fuera la lógica de negocio.

Para qué sirve el middleware

En Lemonade, middleware es una capa PSR-15 alrededor del procesamiento de un request HTTP. Encaja para condiciones del request, cabeceras, CSRF o controles de acceso antes del controller. Mantén clara la frontera: middleware = comportamiento HTTP compartido, controller = caso de uso HTTP concreto y orquestación, service = operaciones de aplicación o negocio. Para un ejemplo completo de formulario con CSRF, continúa con Formularios y flujo POST.

Asigna middleware a una ruta o grupo

Usa middleware de ruta cuando un endpoint necesita una regla HTTP adicional. Usa middleware de grupo cuando varias rutas comparten la misma regla. En ambos casos registras las clases por nombre y el runtime las resuelve desde el container antes 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);
};

Orden y finalización anticipada

El middleware se ejecuta en orden de registro. El orden importa porque una capa puede modificar el request, añadir cabeceras a la response o terminar el pipeline antes de tiempo. return $handler->handle($request); pasa el request a la siguiente capa. Devolver tu propia response termina esa rama sin ejecutar el resto del 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();

Escribe un middleware pequeño

Un middleware propio implementa MiddlewareInterface y recibe dependencias mediante constructor injection. Mantenlo pequeño y centrado en HTTP. Si empieza a gestionar persistencia o lógica de negocio reutilizable, mueve ese trabajo a un servicio de aplicación.

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);
    }
}