CakePHP 3.6: маршруты и промежуточное ПО - PullRequest
0 голосов
/ 28 июня 2018

У меня есть плагин cakephp , который создает миниатюры изображений.
В настоящее время миниатюры «обслуживаются» действием контроллера, который возвращает файл в качестве ответа ( здесь ).

Это маршрут:

Router::plugin(THUMBER, ['path' => '/thumb'], function (RouteBuilder $routes) {
    $routes->get('/:basename', ['controller' => 'Thumbs', 'action' => 'thumb'], 'thumb')
        ->setPatterns(['basename' => '[\w\d=]+'])
        ->setPass(['basename']);
});

Таким образом, URL-адрес (базовое имя миниатюры закодировано):

/thumb/ZDc1NTYyMGY1N2VmMzRiNTQyZjE0NTY2Mjk0YWQ2NTFfNGIyYTBkMjVhMTdjZTdjN2E4MjVjY2M1YWU1ODNhMzcuZ2lm

Теперь я пытаюсь заменить контроллер промежуточным программным обеспечением.
Это довольно просто, потому что в основном он будет работать как AssetMiddleware, а метод __invoke() почти как старый метод действия:

class ThumbnailMiddleware
{
    use ThumbsPathTrait;

    public function __invoke($request, $response, $next)
    {
        if ($request->getParam('_name') !== 'thumb' || !$request->getParam('basename')) {
            return $next($request, $response);
        }

        $file = $this->getPath(base64_decode($request->getParam('basename')));

        if (!is_readable($file)) {
            throw new ThumbNotFoundException(__d('thumber', 'File `{0}` doesn\'t exist', $file));
        }

        $response = $response->withModified(filemtime($file));

        if ($response->checkNotModified($request)) {
            return $response;
        }

        return $response->withFile($file)->withType(mime_content_type($file));
    }
}

Это работает очень хорошо.
Проблема в том, что теперь он работает, потому что это промежуточное ПО берет маршрут контроллера и «перехватывает» запрос, «решая» его, не проходя через контроллер (см. Первые три строки метода __invoke()).

Теперь я хотел бы переписать этот маршрут и отвязать его от контроллера, который я должен полностью исключить из плагина.

Очевидно, что это хорошо работает следующим образом (второй параметр null):

$routes->get('/:basename', null, 'thumb')
    ->setPatterns(['basename' => '[\w\d=]+'])
    ->setPass(['basename']);

Или я мог бы просто вызвать метод RouteBuilder::fallback() и проанализировать URL запроса (как это происходит для AssetMiddleware).

Но мне было интересно, есть ли способ связать маршрут только с промежуточным программным обеспечением и явно для промежуточного программного обеспечения. Или, если нет, какой метод самый лучший. Я знаю, что могу применить «промежуточное ПО для определенных областей маршрутизации» ( cookbook ), поэтому мне интересно, действительно ли это правильная формула:

Router::plugin(THUMBER, ['path' => '/thumb'], function (RouteBuilder $routes) {
    $routes->registerMiddleware('thumbnail', new ThumbnailMiddleware);
    $routes->applyMiddleware('thumbnail');
    $routes->get('/:basename', null, 'thumb')
        ->setPatterns(['basename' => '[\w\d=]+'])
        ->setPass(['basename']);
});

1 Ответ

0 голосов
/ 28 июня 2018

Ну, вам не нужно передавать какие-либо значения по умолчанию для маршрута, поэтому с этой точки зрения это правильно, маршрут не будет «привязан» к контроллеру. Если ваше промежуточное программное обеспечение не будет перехватывать запрос, то в конечном итоге будет выдано MissingControllerException, поскольку диспетчер получит null в качестве имени контроллера. Результирующее сообщение об ошибке, вероятно, будет немного вводить в заблуждение, поскольку не будет никакого имени контроллера для включения.

То, что вы делаете там, заставит ваше промежуточное ПО применяться ко всем маршрутам в области действия /thumb, поэтому, если когда-либо будут какие-либо другие маршруты, тогда ваше промежуточное ПО потребует соответствующих проверок параметров. Вы можете еще больше ограничить ситуацию, применив промежуточное ПО к этому конкретному маршруту, а не к построителю маршрутов:

// $routes->applyMiddleware('thumbnail'); // don't do that
$routes
    ->get('/:basename', null, 'thumb')
    ->setPatterns(['basename' => '[\w\d=]+'])
    ->setPass(['basename'])
    ->setMiddleware(['thumbnail']); // do this instead

Таким образом ваше промежуточное ПО будет вызываться только для этого конкретного маршрута.

...