CakePHP3.6:路由和中间件

CakePHP3.6:路由和中间件,cakephp,cakephp-3.x,Cakephp,Cakephp 3.x,我有一个可以创建图像缩略图的cakephp。 目前,缩略图由控制器的一个操作“提供”,该操作返回一个文件作为响应() 这是路线: Router::plugin(THUMBER, ['path' => '/thumb'], function (RouteBuilder $routes) { $routes->get('/:basename', ['controller' => 'Thumbs', 'action' => 'thumb'], 'thumb')

我有一个可以创建图像缩略图的cakephp。
目前,缩略图由控制器的一个操作“提供”,该操作返回一个文件作为响应()

这是路线:

Router::plugin(THUMBER, ['path' => '/thumb'], function (RouteBuilder $routes) {
    $routes->get('/:basename', ['controller' => 'Thumbs', 'action' => 'thumb'], 'thumb')
        ->setPatterns(['basename' => '[\w\d=]+'])
        ->setPass(['basename']);
});
因此url如下所示(缩略图的基本名称已编码):

现在我正试图用中间件替换控制器。
这非常简单,因为基本上它的工作原理类似于
AssetMiddleware
,而
\u 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));
    }
}
这很有效。
问题是它现在可以工作,因为该中间件采用控制器的路由并“拦截”请求,“解析”请求而不经过控制器(请参阅
\uu invoke()
方法的前三行)

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

显然,它是这样工作的(第二个参数是
null
):

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

但我想知道是否有一种方法可以将路由只链接到中间件,并显式地链接到中间件。 或者,如果不是,最好的方法是什么。 我知道我可以将“中间件应用于特定路由范围”(middleware to specific routing scopes),所以我想知道这是否绝对是正确的公式:

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

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

这样,您的中间件将仅针对该特定路由进行调用。

非常好,感谢您的解释(有一个键入错误)。只有一个问题:现在,我可以删除
\u invoke()
方法的前三行吗?我想是的,因为中间件只会对该路由起作用。这就是我的意思,是的,这些检查不需要,因为中间件只会对该路由调用。
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']);
});
// $routes->applyMiddleware('thumbnail'); // don't do that
$routes
    ->get('/:basename', null, 'thumb')
    ->setPatterns(['basename' => '[\w\d=]+'])
    ->setPass(['basename'])
    ->setMiddleware(['thumbnail']); // do this instead