Properly handle 404 errors
Use 404 template instead of default Slim error page if the route is not found. Fixes #827
This commit is contained in:
parent
e2dff28b44
commit
d52ab0b1e9
6 changed files with 121 additions and 5 deletions
|
@ -10,6 +10,7 @@
|
||||||
use Shaarli\Feed\FeedBuilder;
|
use Shaarli\Feed\FeedBuilder;
|
||||||
use Shaarli\Formatter\FormatterFactory;
|
use Shaarli\Formatter\FormatterFactory;
|
||||||
use Shaarli\Front\Controller\Visitor\ErrorController;
|
use Shaarli\Front\Controller\Visitor\ErrorController;
|
||||||
|
use Shaarli\Front\Controller\Visitor\ErrorNotFoundController;
|
||||||
use Shaarli\History;
|
use Shaarli\History;
|
||||||
use Shaarli\Http\HttpAccess;
|
use Shaarli\Http\HttpAccess;
|
||||||
use Shaarli\Netscape\NetscapeBookmarkUtils;
|
use Shaarli\Netscape\NetscapeBookmarkUtils;
|
||||||
|
@ -149,6 +150,9 @@ public function build(): ShaarliContainer
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$container['notFoundHandler'] = function (ShaarliContainer $container): ErrorNotFoundController {
|
||||||
|
return new ErrorNotFoundController($container);
|
||||||
|
};
|
||||||
$container['errorHandler'] = function (ShaarliContainer $container): ErrorController {
|
$container['errorHandler'] = function (ShaarliContainer $container): ErrorController {
|
||||||
return new ErrorController($container);
|
return new ErrorController($container);
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,21 +24,22 @@
|
||||||
/**
|
/**
|
||||||
* Extension of Slim container to document the injected objects.
|
* Extension of Slim container to document the injected objects.
|
||||||
*
|
*
|
||||||
* @property string $basePath Shaarli's instance base path (e.g. `/shaarli/`)
|
* @property string $basePath Shaarli's instance base path (e.g. `/shaarli/`)
|
||||||
* @property BookmarkServiceInterface $bookmarkService
|
* @property BookmarkServiceInterface $bookmarkService
|
||||||
* @property CookieManager $cookieManager
|
* @property CookieManager $cookieManager
|
||||||
* @property ConfigManager $conf
|
* @property ConfigManager $conf
|
||||||
* @property mixed[] $environment $_SERVER automatically injected by Slim
|
* @property mixed[] $environment $_SERVER automatically injected by Slim
|
||||||
* @property callable $errorHandler Overrides default Slim exception display
|
* @property callable $errorHandler Overrides default Slim exception display
|
||||||
* @property FeedBuilder $feedBuilder
|
* @property FeedBuilder $feedBuilder
|
||||||
* @property FormatterFactory $formatterFactory
|
* @property FormatterFactory $formatterFactory
|
||||||
* @property History $history
|
* @property History $history
|
||||||
* @property HttpAccess $httpAccess
|
* @property HttpAccess $httpAccess
|
||||||
* @property LoginManager $loginManager
|
* @property LoginManager $loginManager
|
||||||
* @property NetscapeBookmarkUtils $netscapeBookmarkUtils
|
* @property NetscapeBookmarkUtils $netscapeBookmarkUtils
|
||||||
|
* @property callable $notFoundHandler Overrides default Slim exception display
|
||||||
* @property PageBuilder $pageBuilder
|
* @property PageBuilder $pageBuilder
|
||||||
* @property PageCacheManager $pageCacheManager
|
* @property PageCacheManager $pageCacheManager
|
||||||
* @property callable $phpErrorHandler Overrides default Slim PHP error display
|
* @property callable $phpErrorHandler Overrides default Slim PHP error display
|
||||||
* @property PluginManager $pluginManager
|
* @property PluginManager $pluginManager
|
||||||
* @property SessionManager $sessionManager
|
* @property SessionManager $sessionManager
|
||||||
* @property Thumbnailer $thumbnailer
|
* @property Thumbnailer $thumbnailer
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Shaarli\Front\Controller\Visitor;
|
||||||
|
|
||||||
|
use Slim\Http\Request;
|
||||||
|
use Slim\Http\Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller used to render the 404 error page.
|
||||||
|
*/
|
||||||
|
class ErrorNotFoundController extends ShaarliVisitorController
|
||||||
|
{
|
||||||
|
public function __invoke(Request $request, Response $response): Response
|
||||||
|
{
|
||||||
|
// Request from the API
|
||||||
|
if (false !== strpos($request->getRequestTarget(), '/api/v1')) {
|
||||||
|
return $response->withStatus(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is required because the middleware is ignored if the route is not found.
|
||||||
|
$this->container->basePath = rtrim($request->getUri()->getBasePath(), '/');
|
||||||
|
|
||||||
|
$this->assignView('error_message', t('Requested page could not be found.'));
|
||||||
|
|
||||||
|
return $response->withStatus(404)->write($this->render('404'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@
|
||||||
use Shaarli\Feed\FeedBuilder;
|
use Shaarli\Feed\FeedBuilder;
|
||||||
use Shaarli\Formatter\FormatterFactory;
|
use Shaarli\Formatter\FormatterFactory;
|
||||||
use Shaarli\Front\Controller\Visitor\ErrorController;
|
use Shaarli\Front\Controller\Visitor\ErrorController;
|
||||||
|
use Shaarli\Front\Controller\Visitor\ErrorNotFoundController;
|
||||||
use Shaarli\History;
|
use Shaarli\History;
|
||||||
use Shaarli\Http\HttpAccess;
|
use Shaarli\Http\HttpAccess;
|
||||||
use Shaarli\Netscape\NetscapeBookmarkUtils;
|
use Shaarli\Netscape\NetscapeBookmarkUtils;
|
||||||
|
@ -75,6 +76,7 @@ public function testBuildContainer(): void
|
||||||
static::assertInstanceOf(PageBuilder::class, $container->pageBuilder);
|
static::assertInstanceOf(PageBuilder::class, $container->pageBuilder);
|
||||||
static::assertInstanceOf(PageCacheManager::class, $container->pageCacheManager);
|
static::assertInstanceOf(PageCacheManager::class, $container->pageCacheManager);
|
||||||
static::assertInstanceOf(ErrorController::class, $container->phpErrorHandler);
|
static::assertInstanceOf(ErrorController::class, $container->phpErrorHandler);
|
||||||
|
static::assertInstanceOf(ErrorNotFoundController::class, $container->notFoundHandler);
|
||||||
static::assertInstanceOf(PluginManager::class, $container->pluginManager);
|
static::assertInstanceOf(PluginManager::class, $container->pluginManager);
|
||||||
static::assertInstanceOf(SessionManager::class, $container->sessionManager);
|
static::assertInstanceOf(SessionManager::class, $container->sessionManager);
|
||||||
static::assertInstanceOf(Thumbnailer::class, $container->thumbnailer);
|
static::assertInstanceOf(Thumbnailer::class, $container->thumbnailer);
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Shaarli\Front\Controller\Visitor;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Slim\Http\Request;
|
||||||
|
use Slim\Http\Response;
|
||||||
|
use Slim\Http\Uri;
|
||||||
|
|
||||||
|
class ErrorNotFoundControllerTest extends TestCase
|
||||||
|
{
|
||||||
|
use FrontControllerMockHelper;
|
||||||
|
|
||||||
|
/** @var ErrorNotFoundController */
|
||||||
|
protected $controller;
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
$this->createContainer();
|
||||||
|
|
||||||
|
$this->controller = new ErrorNotFoundController($this->container);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test displaying 404 error
|
||||||
|
*/
|
||||||
|
public function testDisplayNotFoundError(): void
|
||||||
|
{
|
||||||
|
$request = $this->createMock(Request::class);
|
||||||
|
$request->expects(static::once())->method('getRequestTarget')->willReturn('/');
|
||||||
|
$request->method('getUri')->willReturnCallback(function (): Uri {
|
||||||
|
$uri = $this->createMock(Uri::class);
|
||||||
|
$uri->method('getBasePath')->willReturn('/subfolder');
|
||||||
|
|
||||||
|
return $uri;
|
||||||
|
});
|
||||||
|
|
||||||
|
$response = new Response();
|
||||||
|
|
||||||
|
// Save RainTPL assigned variables
|
||||||
|
$assignedVariables = [];
|
||||||
|
$this->assignTemplateVars($assignedVariables);
|
||||||
|
|
||||||
|
$result = ($this->controller)(
|
||||||
|
$request,
|
||||||
|
$response
|
||||||
|
);
|
||||||
|
|
||||||
|
static::assertSame(404, $result->getStatusCode());
|
||||||
|
static::assertSame('404', (string) $result->getBody());
|
||||||
|
static::assertSame('Requested page could not be found.', $assignedVariables['error_message']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test displaying 404 error from REST API
|
||||||
|
*/
|
||||||
|
public function testDisplayNotFoundErrorFromAPI(): void
|
||||||
|
{
|
||||||
|
$request = $this->createMock(Request::class);
|
||||||
|
$request->expects(static::once())->method('getRequestTarget')->willReturn('/sufolder/api/v1/links');
|
||||||
|
$request->method('getUri')->willReturnCallback(function (): Uri {
|
||||||
|
$uri = $this->createMock(Uri::class);
|
||||||
|
$uri->method('getBasePath')->willReturn('/subfolder');
|
||||||
|
|
||||||
|
return $uri;
|
||||||
|
});
|
||||||
|
|
||||||
|
$response = new Response();
|
||||||
|
|
||||||
|
// Save RainTPL assigned variables
|
||||||
|
$assignedVariables = [];
|
||||||
|
$this->assignTemplateVars($assignedVariables);
|
||||||
|
|
||||||
|
$result = ($this->controller)($request, $response);
|
||||||
|
|
||||||
|
static::assertSame(404, $result->getStatusCode());
|
||||||
|
static::assertSame([], $assignedVariables);
|
||||||
|
}
|
||||||
|
}
|
|
@ -94,7 +94,6 @@ protected function createContainer(): void
|
||||||
protected function assignTemplateVars(array &$variables): void
|
protected function assignTemplateVars(array &$variables): void
|
||||||
{
|
{
|
||||||
$this->container->pageBuilder
|
$this->container->pageBuilder
|
||||||
->expects(static::atLeastOnce())
|
|
||||||
->method('assign')
|
->method('assign')
|
||||||
->willReturnCallback(function ($key, $value) use (&$variables) {
|
->willReturnCallback(function ($key, $value) use (&$variables) {
|
||||||
$variables[$key] = $value;
|
$variables[$key] = $value;
|
||||||
|
|
Loading…
Reference in a new issue