CakePHP 3.6:路由和中间件

时间:2018-06-28 13:23:02

标签: cakephp cakephp-3.x

我有一个cakephp plugin,用于创建图像缩略图。
当前,缩略图是由控制器的操作“提供”的,该控制器返回文件作为响应(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()方法几乎就像旧的action方法一样:

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 :(得分:2)

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

那样,您的中间件将仅针对该特定路由被调用。