Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Посредники

Посредник (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, не знает маршрута и работает неправильно. Добавляя посредник плагина, ставьте его туда, куда велит его документация, — обычно после маршрутов.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.