CakePHP 3.6:路由和中间件

CakePHP 3.6: routes and middleware

我有一个 cakephp plugin 可以创建图像缩略图。
目前,缩略图是 "served" 控制器的一个动作,returns 一个文件作为响应 (here)。

这是路线:

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));
    }
}

这个效果很好。
问题是它现在可以工作了,因为这个中间件采用了控制器的路由和 "intercepts" 请求,"resolving" 它没有经过控制器(参见 __invoke() 方法的前三行)。

现在我想重写该路由并将其从控制器中解开,我应该从插件中完全删除它。

显然这样效果很好(第二个参数是null):

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

或者我可以调用 RouteBuilder::fallback() 方法并分析请求的 url(就像 AssetMiddleware 一样)。

但我想知道是否有办法 link 只到中间件的路由并且明确地用于中间件。 或者,如果没有,最好的方法是什么。 我知道我可以应用 "middleware to specific routing scopes" (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']);
});

好吧,您不需要将任何默认值传递给路由,因此从这个角度来看它是正确的,路由不会 "bound" 到控制器。如果您的中间件不会拦截请求,那么事情将以抛出 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

这样你的中间件只会被那个特定的路由调用。