Middleware
Middleware — PSR-15 шар між маршрутизацією та handler: фільтрує запит, додає контекст,
перехоплює винятки або змінює відповідь. Concept Core використовує
Psr\Http\Server\MiddlewareInterface і League Route
lazyMiddleware().
Middleware — не те саме, що route interceptors:
interceptors спрацьовують у RouteStrategy після middleware, до виклику handler,
і не формують PSR-15 ланцюжок.
Див. також: життєвий цикл → middleware, реєстрація на маршрутах.
Контракт PSR-15
<?php declare(strict_types=1);
namespace Concept\App\Http\Middlewares;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
final class ExampleMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
// До handler: перевірки, зміна $request
$response = $handler->handle($request);
// Після handler: заголовки, логування
return $response;
}
}
Клас реєструється в контейнері; League Route створює екземпляр через DI при
lazyMiddleware(MyMiddleware::class) — залежності конструктора підставляються
автоматично (ReflectionContainer).
Де підключати
- Глобально — на початку
routes/web.php(усі маршрути файлу). - Група —
$router->group(...)->lazyMiddleware(...). - Один маршрут — ланцюжок після
get()/post(). - Компонент — у
Component/routes.php(наприкладAuthMiddlewareна групі/admin).
Порядок реєстрації визначає порядок обгортання: перший доданий middleware — зовнішній шар (виконується першим на вході). Мінімальний skeleton не підключає глобальний стек;
Middleware ядра
Класи в Concept\Core\Http\Middlewares:
| Клас | Призначення |
|---|---|
HandleValidationExceptionMiddleware |
Ловить ValidationException з FormRequest: JSON 422 (RequestFormat::expectsJson()) або flash + ResponseFactory::back() |
VerifyCsrfTokenMiddleware |
CSRF для POST, PUT, PATCH, DELETE; при помилці — CsrfException |
StorePreviousUrlMiddleware |
На GET (не AJAX) зберігає _url_current / _url_previous у сесії; задає Concept\Core\Http\Requests\RequestAttribute::SAFE_BACK_URL |
ShareViewDataMiddleware |
Flash, помилки валідації, old input, csrf_token → Concept\Core\Http\Requests\RequestAttribute::VIEW_PAYLOAD |
ParseJsonBodyMiddleware |
Якщо Content-Type містить json — парсить body у getParsedBody() |
ForceJsonResponseMiddleware |
Встановлює Accept: application/json для внутрішньої логіки; у відповіді — Content-Type: application/json |
Типовий глобальний стек
$router->lazyMiddleware(HandleValidationExceptionMiddleware::class);
$router->lazyMiddleware(VerifyCsrfTokenMiddleware::class);
$router->lazyMiddleware(StorePreviousUrlMiddleware::class);
$router->lazyMiddleware(ShareViewDataMiddleware::class);
- Зовнішній try/catch валідації (обгортає весь ланцюжок)
- Перевірка CSRF до зміни стану
- Фіксація URL перед рендером
- Підготовка shared-даних для Twig (
ViewResponseFactory)
CSRF
Токен зберігається в сесії під ключем SessionKey::CSRF_TOKEN (_csrf_token)
через CsrfTokenManager. У формах — поле _csrf_token;
для AJAX — заголовки X-CSRF-TOKEN або X-XSRF-TOKEN
(константи HttpHeader).
<input type="hidden" name="_csrf_token" value="{{ csrf_token }}">
У Twig змінна csrf_token (ViewKey::CSRF_TOKEN) потрапляє через
ShareViewDataMiddleware → ViewResponseFactory::create().
Детальніше — Захист CSRF.
Middleware авторизації
У застосунку — власні класи на групах маршрутів:
AuthMiddleware(AuthAdmin) — сесія, роль admin, статусACTIVE; redirect на/admin/loginActiveUserMiddleware(UserCabinet) — авторизація, статуси pending/blocked;redirectByName('cabinet.login')
$router->group('/admin', function (RouteGroup $route) {
// ...
})->lazyMiddleware(AuthMiddleware::class);
$router->group('/cabinet', function (RouteGroup $route) {
// ...
})->lazyMiddleware(ActiveUserMiddleware::class);
При відмові контролер не виконується — middleware повертає redirect-відповідь.
Атрибути запиту
Middleware передає дані далі через $request->withAttribute():
| Константа | Хто задає | Хто читає |
|---|---|---|
Concept\Core\Http\Requests\RequestAttribute::VIEW_PAYLOAD |
ShareViewDataMiddleware |
ViewResponseFactory → ViewInterface::share() |
Concept\Core\Http\Requests\RequestAttribute::SAFE_BACK_URL |
StorePreviousUrlMiddleware |
ResponseFactory::back() |
Ключі всередині VIEW_PAYLOAD (ViewKey): errors, old, flashes, csrf_token.
ResponseFactory::back() спочатку перевіряє заголовок Referer,
потім SAFE_BACK_URL, і лише внутрішні URL (див. безпечні редіректи).
Власний middleware
- Створіть клас з
MiddlewareInterface(наприклад уsrc/App/Middlewares/або в компоненті). - Залежності — через конструктор (контейнер підставить автоматично).
- Підключіть
lazyMiddleware()глобально, на групу або маршрут. - Перевірте порядок відносно CSRF, auth і
HandleValidationExceptionMiddleware.
Middleware має бути «вузьким»: одна відповідальність. Складну бізнес-логіку краще в сервісі або контролері.
Перегляд middleware
php bin/console.php route:list
php bin/console.php route:list --full-middleware
php bin/console.php route:list -F
Колонка middleware показує глобальні та локальні класи для кожного маршруту.