Посредники
Посредник (middleware) — класс, через который проходит каждый запрос по пути к контроллеру и каждый ответ по пути обратно. Посредники выстроены в очередь: один разбирает маршрут, другой проверяет токен формы, третий ловит ошибки. Так общие для всех страниц задачи решаются до контроллера.
Где находится очередь
Очередь описана в методе middleware() файла src/Application.php. В новом проекте она уже заполнена:
| Посредник | Что делает |
|---|---|
ErrorHandlerMiddleware | Превращает исключения в страницы ошибок |
AssetMiddleware | Отдаёт файлы плагинов |
RoutingMiddleware | Находит маршрут для адреса |
BodyParserMiddleware | Разбирает тело запроса в формате JSON |
CsrfProtectionMiddleware | Проверяет токен форм |
Создание
Свои посредники лежат в папке src/Middleware:
bin/cake bake middleware Timer// src/Middleware/TimerMiddleware.php
namespace App\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class TimerMiddleware implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
return $handler->handle($request);
}
}Это посредник, который ничего не делает. $handler->handle($request) передаёт запрос дальше по очереди и возвращает готовый ответ.
Код до и после
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$start = microtime(true);
$response = $handler->handle($request);
$ms = (int) round((microtime(true) - $start) * 1000);
return $response->withHeader('X-Response-Time', $ms . 'ms');
}Всё, что написано до вызова handle(), выполняется до контроллера, всё после — когда ответ уже готов. Здесь посредник измеряет время и добавляет его в заголовок ответа.
Подключение
// src/Application.php
use App\Middleware\TimerMiddleware;
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
$middlewareQueue
// ... посредники, которые уже были в файле
->add(new TimerMiddleware());
return $middlewareQueue;
}add() ставит посредник в конец очереди. Откройте любую страницу и посмотрите заголовки ответа на вкладке «Сеть» в инструментах браузера — появится строка X-Response-Time.
Ответ без контроллера
Посредник может не пускать запрос дальше и ответить сам:
use Cake\Http\Response;
if ($request->getUri()->getPath() === '/ping') {
return (new Response())->withStringBody('pong');
}
return $handler->handle($request);По адресу /ping придёт текст pong, а до маршрутов и контроллеров дело не дойдёт. Так устроены проверки доступа и режим обслуживания.
Только для части адресов
Посредник можно включить для одной группы маршрутов:
// config/routes.php
$routes->scope('/api', function (RouteBuilder $builder): void {
$builder->registerMiddleware('timer', new TimerMiddleware());
$builder->applyMiddleware('timer');
$builder->resources('Articles');
});registerMiddleware() даёт посреднику имя, applyMiddleware() применяет его ко всем маршрутам этой группы.
Частая ошибка
Порядок в очереди важен: посредник видит только то, что сделали посредники перед ним. AuthenticationMiddleware, поставленный раньше RoutingMiddleware, не знает маршрута и работает неправильно. Добавляя посредник плагина, ставьте его туда, куда велит его документация, — обычно после маршрутов.