Руководство

Middleware

Используйте middleware для общих HTTP-задач, подключайте его к маршрутам или группам и не помещайте туда бизнес-логику.

Для чего нужен middleware

В Lemonade middleware — это слой PSR-15 вокруг обработки HTTP request. Он подходит для условий над request, заголовков, CSRF или проверки доступа до controller. Границу держите ясной: middleware = общее HTTP-поведение, controller = конкретный HTTP use case и orchestration, service = прикладные или бизнес-операции. Полный пример формы с CSRF смотрите в разделе Формы и POST-поток.

Подключайте middleware к маршруту или группе

Используйте middleware маршрута, когда дополнительное HTTP-правило нужно одному endpoint. Group middleware подходит, когда одно правило разделяют несколько маршрутов. В обоих случаях вы регистрируете классы по имени, а runtime разрешает их из container до 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);
};

Порядок и досрочное завершение

Middleware выполняются в порядке регистрации. Порядок важен: один слой может изменить request, добавить заголовки в response или завершить pipeline раньше. return $handler->handle($request); передаёт request дальше. Возврат собственной response завершает текущую ветку без выполнения оставшейся части 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();

Напишите небольшой собственный middleware

Собственный middleware реализует MiddlewareInterface и получает зависимости через constructor injection. Держите его небольшим и сфокусированным на HTTP. Если он начинает заниматься persistence или повторно используемой бизнес-логикой, перенесите эту работу в сервис приложения.

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