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.
<?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.
<?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 или повторно используемой бизнес-логикой, перенесите эту работу в сервис приложения.
<?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);
}
}