title: CakePHP 3.6: routes and middleware
body: <p>I have a cakephp <a href="https://github.com/mirko-pagliai/cakephp-thumber" rel="nofollow noreferrer">plugin</a> that creates image thumbnails.<br>
  Currently, thumbnails are "served" by an action of a controller that returns a file as response (<a href="https://github.com/mirko-pagliai/cakephp-thumber/blob/master/src/Controller/ThumbsController.php" rel="nofollow noreferrer">here</a>).</p>
  
  <p>This is the route:</p>
  
  <pre><code>Router::plugin(THUMBER, ['path' =&gt; '/thumb'], function (RouteBuilder $routes) {
      $routes-&gt;get('/:basename', ['controller' =&gt; 'Thumbs', 'action' =&gt; 'thumb'], 'thumb')
          -&gt;setPatterns(['basename' =&gt; '[\w\d=]+'])
          -&gt;setPass(['basename']);
  });
  </code></pre>
  
  <p>So the url are these (the thumbnail basename is encoded):</p>
  
  <pre><code>/thumb/ZDc1NTYyMGY1N2VmMzRiNTQyZjE0NTY2Mjk0YWQ2NTFfNGIyYTBkMjVhMTdjZTdjN2E4MjVjY2M1YWU1ODNhMzcuZ2lm
  </code></pre>
  
  <p>Now I'm trying to replace the controller with a middleware.<br>
  This is quite simple, because basically it would work like the <code>AssetMiddleware</code> and the <code>__invoke()</code> method is almost like the old action method:</p>
  
  <pre><code>class ThumbnailMiddleware
  {
      use ThumbsPathTrait;
  
      public function __invoke($request, $response, $next)
      {
          if ($request-&gt;getParam('_name') !== 'thumb' || !$request-&gt;getParam('basename')) {
              return $next($request, $response);
          }
  
          $file = $this-&gt;getPath(base64_decode($request-&gt;getParam('basename')));
  
          if (!is_readable($file)) {
              throw new ThumbNotFoundException(__d('thumber', 'File `{0}` doesn\'t exist', $file));
          }
  
          $response = $response-&gt;withModified(filemtime($file));
  
          if ($response-&gt;checkNotModified($request)) {
              return $response;
          }
  
          return $response-&gt;withFile($file)-&gt;withType(mime_content_type($file));
      }
  }
  </code></pre>
  
  <p>This works very well.<br>
  The problem is that it now works because this middleware takes the controller's route and "intercepts" the request, "resolving" it without going through the controller (see the first three lines of the <code>__invoke()</code> method).</p>
  
  <p>Now I would like to rewrite that route and untie it from the controller, which I should totally eliminate from the plugin.</p>
  
  <p>Obviously it works well like this (the second parameter is <code>null</code>):</p>
  
  <pre><code>$routes-&gt;get('/:basename', null, 'thumb')
      -&gt;setPatterns(['basename' =&gt; '[\w\d=]+'])
      -&gt;setPass(['basename']);
  </code></pre>
  
  <p>Or I could just call the <code>RouteBuilder::fallback()</code> method and analyze the url of the request (as it happens for the <code>AssetMiddleware</code>).</p>
  
  <p>But I was wondering if there is a way to link a route only to a middleware and explicitly for a middleware.
  Or, if not, what is the best method.
  I know that I can apply "middleware to specific routing scopes" (<a href="https://book.cakephp.org/3.0/en/development/routing.html#connecting-scoped-middleware" rel="nofollow noreferrer">cookbook</a>), so I'm wondering if this is definitely the correct formula:</p>
  
  <pre><code>Router::plugin(THUMBER, ['path' =&gt; '/thumb'], function (RouteBuilder $routes) {
      $routes-&gt;registerMiddleware('thumbnail', new ThumbnailMiddleware);
      $routes-&gt;applyMiddleware('thumbnail');
      $routes-&gt;get('/:basename', null, 'thumb')
          -&gt;setPatterns(['basename' =&gt; '[\w\d=]+'])
          -&gt;setPass(['basename']);
  });
  </code></pre>