Browse Source

Implement a PSR7 routing middleware

This middleware applies routing rules to the incoming request and
parses the CakePHP parameters out.
Mark Story 10 years ago
parent
commit
637b9948e7

+ 53 - 0
src/Http/Middleware/RoutingMiddleware.php

@@ -0,0 +1,53 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @since         3.3.0
+ * @license       http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Http\Middleware;
+
+use Cake\Routing\Exception\RedirectException;
+use Cake\Routing\Router;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Zend\Diactoros\Response\RedirectResponse;
+
+/**
+ * Applies routing rules to the request and creates the controller
+ * instance if possible.
+ */
+class RoutingMiddleware
+{
+
+    /**
+     * @param ServerRequestInterface $request  The request.
+     * @param ResponseInterface $response The response.
+     * @param callable $next The next middleware to call
+     * @return \Psr\Http\Message\ResponseInterface A response
+     */
+    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
+    {
+        try {
+            $params = (array)$request->getAttribute('params', []);
+            if (empty($params['controller'])) {
+                $path = $request->getUri()->getPath();
+                $request = $request->withAttribute('params', Router::parse($path, $request->getMethod()));
+            }
+        } catch (RedirectException $e) {
+            return new RedirectResponse(
+                $e->getMessage(),
+                $e->getCode(),
+                $response->getHeaders()
+            );
+        }
+        return $next($request, $response);
+    }
+}

+ 133 - 0
tests/TestCase/Http/Middleware/RoutingMiddlewareTest.php

@@ -0,0 +1,133 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @since         3.3.0
+ * @license       http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Test\TestCase\Http\Middleware;
+
+use Cake\Http\Middleware\RoutingMiddleware;
+use Cake\Routing\Router;
+use Cake\TestSuite\TestCase;
+use Zend\Diactoros\Request;
+use Zend\Diactoros\Response;
+use Zend\Diactoros\ServerRequestFactory;
+
+/**
+ * Test for RoutingMiddleware
+ */
+class RoutingMiddlewareTest extends TestCase
+{
+    /**
+     * Setup method
+     *
+     * @return void
+     */
+    public function setUp()
+    {
+        parent::setUp();
+        Router::reload();
+        Router::connect('/articles', ['controller' => 'Articles', 'action' => 'index']);
+    }
+
+    /**
+     * Test redirect responses from redirect routes
+     *
+     * @return void
+     */
+    public function testRedirectResponse()
+    {
+        Router::redirect('/testpath', '/pages');
+        $request = ServerRequestFactory::fromGlobals(['REQUEST_URI' => '/testpath']);
+        $response = new Response();
+        $next = function ($req, $res) {
+        };
+        $middleware = new RoutingMiddleware();
+        $response = $middleware($request, $response, $next);
+
+        $this->assertEquals(301, $response->getStatusCode());
+        $this->assertEquals('http://localhost/pages', $response->getHeaderLine('Location'));
+    }
+
+    /**
+     * Test redirects with additional headers
+     *
+     * @return void
+     */
+    public function testRedirectResponseWithHeaders()
+    {
+        Router::redirect('/testpath', '/pages');
+        $request = ServerRequestFactory::fromGlobals(['REQUEST_URI' => '/testpath']);
+        $response = new Response('php://memory', 200, ['X-testing' => 'Yes']);
+        $next = function ($req, $res) {
+        };
+        $middleware = new RoutingMiddleware();
+        $response = $middleware($request, $response, $next);
+
+        $this->assertEquals(301, $response->getStatusCode());
+        $this->assertEquals('http://localhost/pages', $response->getHeaderLine('Location'));
+        $this->assertEquals('Yes', $response->getHeaderLine('X-testing'));
+    }
+
+    /**
+     * Test that Router sets parameters
+     *
+     * @return void
+     */
+    public function testRouterSetParams()
+    {
+        $request = ServerRequestFactory::fromGlobals(['REQUEST_URI' => '/articles']);
+        $response = new Response();
+        $next = function ($req, $res) {
+            $expected = [
+                'controller' => 'Articles',
+                'action' => 'index',
+                'plugin' => null,
+                'pass' => []
+            ];
+            $this->assertEquals($expected, $req->getAttribute('params'));
+        };
+        $middleware = new RoutingMiddleware();
+        $middleware($request, $response, $next);
+    }
+
+    /**
+     * Test that routing is not applied if a controller exists already
+     *
+     * @return void
+     */
+    public function testRouterNoopOnController()
+    {
+        $request = ServerRequestFactory::fromGlobals(['REQUEST_URI' => '/articles']);
+        $request = $request->withAttribute('params', ['controller' => 'Articles']);
+        $response = new Response();
+        $next = function ($req, $res) {
+            $this->assertEquals(['controller' => 'Articles'], $req->getAttribute('params'));
+        };
+        $middleware = new RoutingMiddleware();
+        $middleware($request, $response, $next);
+    }
+
+    /**
+     * Test missing routes not being caught.
+     *
+     * @expectedException \Cake\Routing\Exception\MissingRouteException
+     */
+    public function testMissingRouteNotCaught()
+    {
+        $request = ServerRequestFactory::fromGlobals(['REQUEST_URI' => '/missing']);
+        $response = new Response();
+        $next = function ($req, $res) {
+        };
+        $middleware = new RoutingMiddleware();
+        $middleware($request, $response, $next);
+    }
+}