diff options
author | ArthurHoaro <arthur@hoa.ro> | 2020-09-12 12:42:19 +0200 |
---|---|---|
committer | ArthurHoaro <arthur@hoa.ro> | 2020-09-12 12:42:19 +0200 |
commit | d52ab0b1e99aa0c494f389092dce1e926296032d (patch) | |
tree | 3a436eedf30cbb8f8b0943c5e5c3b88e49488b69 | |
parent | e2dff28b44fafcf11a1db7985c50cd40e6945821 (diff) | |
download | Shaarli-d52ab0b1e99aa0c494f389092dce1e926296032d.tar.gz Shaarli-d52ab0b1e99aa0c494f389092dce1e926296032d.tar.zst Shaarli-d52ab0b1e99aa0c494f389092dce1e926296032d.zip |
Properly handle 404 errors
Use 404 template instead of default Slim error page if the route is not found.
Fixes #827
6 files changed, 121 insertions, 5 deletions
diff --git a/application/container/ContainerBuilder.php b/application/container/ContainerBuilder.php index 58067c99..55bb51b5 100644 --- a/application/container/ContainerBuilder.php +++ b/application/container/ContainerBuilder.php | |||
@@ -10,6 +10,7 @@ use Shaarli\Config\ConfigManager; | |||
10 | use Shaarli\Feed\FeedBuilder; | 10 | use Shaarli\Feed\FeedBuilder; |
11 | use Shaarli\Formatter\FormatterFactory; | 11 | use Shaarli\Formatter\FormatterFactory; |
12 | use Shaarli\Front\Controller\Visitor\ErrorController; | 12 | use Shaarli\Front\Controller\Visitor\ErrorController; |
13 | use Shaarli\Front\Controller\Visitor\ErrorNotFoundController; | ||
13 | use Shaarli\History; | 14 | use Shaarli\History; |
14 | use Shaarli\Http\HttpAccess; | 15 | use Shaarli\Http\HttpAccess; |
15 | use Shaarli\Netscape\NetscapeBookmarkUtils; | 16 | use Shaarli\Netscape\NetscapeBookmarkUtils; |
@@ -149,6 +150,9 @@ class ContainerBuilder | |||
149 | ); | 150 | ); |
150 | }; | 151 | }; |
151 | 152 | ||
153 | $container['notFoundHandler'] = function (ShaarliContainer $container): ErrorNotFoundController { | ||
154 | return new ErrorNotFoundController($container); | ||
155 | }; | ||
152 | $container['errorHandler'] = function (ShaarliContainer $container): ErrorController { | 156 | $container['errorHandler'] = function (ShaarliContainer $container): ErrorController { |
153 | return new ErrorController($container); | 157 | return new ErrorController($container); |
154 | }; | 158 | }; |
diff --git a/application/container/ShaarliContainer.php b/application/container/ShaarliContainer.php index 9a9a974a..66e669aa 100644 --- a/application/container/ShaarliContainer.php +++ b/application/container/ShaarliContainer.php | |||
@@ -24,21 +24,22 @@ use Slim\Container; | |||
24 | /** | 24 | /** |
25 | * Extension of Slim container to document the injected objects. | 25 | * Extension of Slim container to document the injected objects. |
26 | * | 26 | * |
27 | * @property string $basePath Shaarli's instance base path (e.g. `/shaarli/`) | 27 | * @property string $basePath Shaarli's instance base path (e.g. `/shaarli/`) |
28 | * @property BookmarkServiceInterface $bookmarkService | 28 | * @property BookmarkServiceInterface $bookmarkService |
29 | * @property CookieManager $cookieManager | 29 | * @property CookieManager $cookieManager |
30 | * @property ConfigManager $conf | 30 | * @property ConfigManager $conf |
31 | * @property mixed[] $environment $_SERVER automatically injected by Slim | 31 | * @property mixed[] $environment $_SERVER automatically injected by Slim |
32 | * @property callable $errorHandler Overrides default Slim exception display | 32 | * @property callable $errorHandler Overrides default Slim exception display |
33 | * @property FeedBuilder $feedBuilder | 33 | * @property FeedBuilder $feedBuilder |
34 | * @property FormatterFactory $formatterFactory | 34 | * @property FormatterFactory $formatterFactory |
35 | * @property History $history | 35 | * @property History $history |
36 | * @property HttpAccess $httpAccess | 36 | * @property HttpAccess $httpAccess |
37 | * @property LoginManager $loginManager | 37 | * @property LoginManager $loginManager |
38 | * @property NetscapeBookmarkUtils $netscapeBookmarkUtils | 38 | * @property NetscapeBookmarkUtils $netscapeBookmarkUtils |
39 | * @property callable $notFoundHandler Overrides default Slim exception display | ||
39 | * @property PageBuilder $pageBuilder | 40 | * @property PageBuilder $pageBuilder |
40 | * @property PageCacheManager $pageCacheManager | 41 | * @property PageCacheManager $pageCacheManager |
41 | * @property callable $phpErrorHandler Overrides default Slim PHP error display | 42 | * @property callable $phpErrorHandler Overrides default Slim PHP error display |
42 | * @property PluginManager $pluginManager | 43 | * @property PluginManager $pluginManager |
43 | * @property SessionManager $sessionManager | 44 | * @property SessionManager $sessionManager |
44 | * @property Thumbnailer $thumbnailer | 45 | * @property Thumbnailer $thumbnailer |
diff --git a/application/front/controller/visitor/ErrorNotFoundController.php b/application/front/controller/visitor/ErrorNotFoundController.php new file mode 100644 index 00000000..758dd83b --- /dev/null +++ b/application/front/controller/visitor/ErrorNotFoundController.php | |||
@@ -0,0 +1,29 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Shaarli\Front\Controller\Visitor; | ||
6 | |||
7 | use Slim\Http\Request; | ||
8 | use Slim\Http\Response; | ||
9 | |||
10 | /** | ||
11 | * Controller used to render the 404 error page. | ||
12 | */ | ||
13 | class ErrorNotFoundController extends ShaarliVisitorController | ||
14 | { | ||
15 | public function __invoke(Request $request, Response $response): Response | ||
16 | { | ||
17 | // Request from the API | ||
18 | if (false !== strpos($request->getRequestTarget(), '/api/v1')) { | ||
19 | return $response->withStatus(404); | ||
20 | } | ||
21 | |||
22 | // This is required because the middleware is ignored if the route is not found. | ||
23 | $this->container->basePath = rtrim($request->getUri()->getBasePath(), '/'); | ||
24 | |||
25 | $this->assignView('error_message', t('Requested page could not be found.')); | ||
26 | |||
27 | return $response->withStatus(404)->write($this->render('404')); | ||
28 | } | ||
29 | } | ||
diff --git a/tests/container/ContainerBuilderTest.php b/tests/container/ContainerBuilderTest.php index c08010ae..2047a63a 100644 --- a/tests/container/ContainerBuilderTest.php +++ b/tests/container/ContainerBuilderTest.php | |||
@@ -10,6 +10,7 @@ use Shaarli\Config\ConfigManager; | |||
10 | use Shaarli\Feed\FeedBuilder; | 10 | use Shaarli\Feed\FeedBuilder; |
11 | use Shaarli\Formatter\FormatterFactory; | 11 | use Shaarli\Formatter\FormatterFactory; |
12 | use Shaarli\Front\Controller\Visitor\ErrorController; | 12 | use Shaarli\Front\Controller\Visitor\ErrorController; |
13 | use Shaarli\Front\Controller\Visitor\ErrorNotFoundController; | ||
13 | use Shaarli\History; | 14 | use Shaarli\History; |
14 | use Shaarli\Http\HttpAccess; | 15 | use Shaarli\Http\HttpAccess; |
15 | use Shaarli\Netscape\NetscapeBookmarkUtils; | 16 | use Shaarli\Netscape\NetscapeBookmarkUtils; |
@@ -75,6 +76,7 @@ class ContainerBuilderTest extends TestCase | |||
75 | static::assertInstanceOf(PageBuilder::class, $container->pageBuilder); | 76 | static::assertInstanceOf(PageBuilder::class, $container->pageBuilder); |
76 | static::assertInstanceOf(PageCacheManager::class, $container->pageCacheManager); | 77 | static::assertInstanceOf(PageCacheManager::class, $container->pageCacheManager); |
77 | static::assertInstanceOf(ErrorController::class, $container->phpErrorHandler); | 78 | static::assertInstanceOf(ErrorController::class, $container->phpErrorHandler); |
79 | static::assertInstanceOf(ErrorNotFoundController::class, $container->notFoundHandler); | ||
78 | static::assertInstanceOf(PluginManager::class, $container->pluginManager); | 80 | static::assertInstanceOf(PluginManager::class, $container->pluginManager); |
79 | static::assertInstanceOf(SessionManager::class, $container->sessionManager); | 81 | static::assertInstanceOf(SessionManager::class, $container->sessionManager); |
80 | static::assertInstanceOf(Thumbnailer::class, $container->thumbnailer); | 82 | static::assertInstanceOf(Thumbnailer::class, $container->thumbnailer); |
diff --git a/tests/front/controller/visitor/ErrorNotFoundControllerTest.php b/tests/front/controller/visitor/ErrorNotFoundControllerTest.php new file mode 100644 index 00000000..625467b1 --- /dev/null +++ b/tests/front/controller/visitor/ErrorNotFoundControllerTest.php | |||
@@ -0,0 +1,81 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Shaarli\Front\Controller\Visitor; | ||
6 | |||
7 | use PHPUnit\Framework\TestCase; | ||
8 | use Slim\Http\Request; | ||
9 | use Slim\Http\Response; | ||
10 | use Slim\Http\Uri; | ||
11 | |||
12 | class ErrorNotFoundControllerTest extends TestCase | ||
13 | { | ||
14 | use FrontControllerMockHelper; | ||
15 | |||
16 | /** @var ErrorNotFoundController */ | ||
17 | protected $controller; | ||
18 | |||
19 | public function setUp(): void | ||
20 | { | ||
21 | $this->createContainer(); | ||
22 | |||
23 | $this->controller = new ErrorNotFoundController($this->container); | ||
24 | } | ||
25 | |||
26 | /** | ||
27 | * Test displaying 404 error | ||
28 | */ | ||
29 | public function testDisplayNotFoundError(): void | ||
30 | { | ||
31 | $request = $this->createMock(Request::class); | ||
32 | $request->expects(static::once())->method('getRequestTarget')->willReturn('/'); | ||
33 | $request->method('getUri')->willReturnCallback(function (): Uri { | ||
34 | $uri = $this->createMock(Uri::class); | ||
35 | $uri->method('getBasePath')->willReturn('/subfolder'); | ||
36 | |||
37 | return $uri; | ||
38 | }); | ||
39 | |||
40 | $response = new Response(); | ||
41 | |||
42 | // Save RainTPL assigned variables | ||
43 | $assignedVariables = []; | ||
44 | $this->assignTemplateVars($assignedVariables); | ||
45 | |||
46 | $result = ($this->controller)( | ||
47 | $request, | ||
48 | $response | ||
49 | ); | ||
50 | |||
51 | static::assertSame(404, $result->getStatusCode()); | ||
52 | static::assertSame('404', (string) $result->getBody()); | ||
53 | static::assertSame('Requested page could not be found.', $assignedVariables['error_message']); | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * Test displaying 404 error from REST API | ||
58 | */ | ||
59 | public function testDisplayNotFoundErrorFromAPI(): void | ||
60 | { | ||
61 | $request = $this->createMock(Request::class); | ||
62 | $request->expects(static::once())->method('getRequestTarget')->willReturn('/sufolder/api/v1/links'); | ||
63 | $request->method('getUri')->willReturnCallback(function (): Uri { | ||
64 | $uri = $this->createMock(Uri::class); | ||
65 | $uri->method('getBasePath')->willReturn('/subfolder'); | ||
66 | |||
67 | return $uri; | ||
68 | }); | ||
69 | |||
70 | $response = new Response(); | ||
71 | |||
72 | // Save RainTPL assigned variables | ||
73 | $assignedVariables = []; | ||
74 | $this->assignTemplateVars($assignedVariables); | ||
75 | |||
76 | $result = ($this->controller)($request, $response); | ||
77 | |||
78 | static::assertSame(404, $result->getStatusCode()); | ||
79 | static::assertSame([], $assignedVariables); | ||
80 | } | ||
81 | } | ||
diff --git a/tests/front/controller/visitor/FrontControllerMockHelper.php b/tests/front/controller/visitor/FrontControllerMockHelper.php index e0bd4ecf..927e7f0a 100644 --- a/tests/front/controller/visitor/FrontControllerMockHelper.php +++ b/tests/front/controller/visitor/FrontControllerMockHelper.php | |||
@@ -94,7 +94,6 @@ trait FrontControllerMockHelper | |||
94 | protected function assignTemplateVars(array &$variables): void | 94 | protected function assignTemplateVars(array &$variables): void |
95 | { | 95 | { |
96 | $this->container->pageBuilder | 96 | $this->container->pageBuilder |
97 | ->expects(static::atLeastOnce()) | ||
98 | ->method('assign') | 97 | ->method('assign') |
99 | ->willReturnCallback(function ($key, $value) use (&$variables) { | 98 | ->willReturnCallback(function ($key, $value) use (&$variables) { |
100 | $variables[$key] = $value; | 99 | $variables[$key] = $value; |