Create templated 404 page in Slim 4 with CallableResolver
In Slim 4 Skeleton, the 404 response handled in App\Application\Handlers\HttpErrorHandler::respond()
which check against Slim\Exception\HttpNotFoundException
. We can create a templated 404 page for it with utilize Slim\CallableResolver
via callableResolver
property which can resolve the callable handler. I assume that we are using Twig template engine, and already has setup of Twig service like in my previous post.
We can create a not found handler like the following in src/Application/Handlers/NotFoundHandler.php
<?php // src/Application/Handlers/NotFoundHandler.php declare(strict_types=1); namespace App\Application\Handlers; use Psr\Http\Message\ResponseInterface; use Slim\Views\Twig; use function compact; class NotFoundHandler { private $view; public function __construct(Twig $view) { $this->view = $view; } public function __invoke( ResponseInterface $response, string $message ): ResponseInterface { return $this->view->render($response, '404.html.twig', compact('message')); } }
We can create a view based on it like the following at templates/404.html.twig
:
{# templates/404.html.twig #} {% extends "layout.html.twig" %} {% block title '404 - '~parent() %} {% block body %} {{ message }} {% endblock %}
Now, in App\Application\Handlers\HttpErrorHandler::respond()
, we can check when $exception instanceof HttpNotFoundException to make a self called resolved NotFoundHandler.
// src/Application/Handlers/HttpErrorHandler.php // ... protected function respond(): Response { // ... if ($exception instanceof HttpNotFoundException) { $response = $this->responseFactory->createResponse($statusCode); return ($this->callableResolver->resolve(NotFoundHandler::class)( $response, $exception->getMessage() )); } // ... } // ...
So, when the Slim\Exception\HttpNotFoundException
thrown, it will shown the 404 page with brought the message passed into it like the following:
Bonus
We can create a handling against Request as well, eg: show 404 templated page only when request doesn’t has Accept: application/json or X-Requested-With:XmlHttpRequest header, so, we can modify like the following:
// src/Application/Handlers/HttpErrorHandler.php // ... protected function respond(): Response { // ... if ($exception instanceof HttpNotFoundException) { $isAppJsonAccept = $this->request->getHeaderLine('Accept') === 'application/json'; $isXmlHttpRequest = $this->request->getHeaderLine('X-Requested-With') === 'XmlHttpRequest'; if (! $isAppJsonAccept && ! $isXmlHttpRequest) { $response = $this->responseFactory->createResponse($statusCode); return ($this->callableResolver->resolve(NotFoundHandler::class)( $response, $exception->getMessage() )); } // already return early, no need else $error->setType(ActionError::RESOURCE_NOT_FOUND); } // ... } // ...
So, for example, when called via ajax, it will show like the following:
leave a comment