Quantcast
Channel: Slim Framework – Rob Allen's DevNotes
Viewing all articles
Browse latest Browse all 38

Custom error rendering in Slim 4

$
0
0

One of the nice things about Slim 4 is that it's easier to customise the HTML generated on error without having to worry about the rest of the error handling mechanism. This is because we have separated error rendering from error handling.

Slim's default ErrorHandler maintains a list of renderers, one for each content-type and will delegate the creation of the error payload (HTML, JSON, XML, etc) to the renderer. Out of the Box, the error hander registers an HtmlErrorRenderer which provides a very basic error display:

Slim4 default error no details

We can also enable the displayErrorDetails setting to view the information about the error:

Slim4 default error with details

Separately, the error handler can log these details to the error log.

Writing a custom HTML renderer

To write a new error renderer, we need to create a classs that implements SLim\Interfaces\ErrorRendererInterface. This interface requires a single method:

public function __invoke(Throwable $exception, bool $displayErrorDetails): string;

We are given the exception that caused this error and have to return the HTML we wish to be displayed.

Let's create our own HTML error renderer. If you want to play along, my Slim4-Starter project is a good place to start from.

We start with a basic HtmlErrorRenderer, which we'll store in the src/Error/Renderer directory:

<?php

declare(strict_types=1);

namespace App\Error\Renderer;

use Slim\Exception\HttpNotFoundException;
use Slim\Interfaces\ErrorRendererInterface;
use Throwable;

final class HtmlErrorRenderer extends ErrorRendererInterface
{
    public function __invoke(Throwable $exception, bool $displayErrorDetails): string
    {
        $title = 'Error';
        $message = 'An error has occurred.';

        if ($exception instanceof HttpNotFoundException) {
            $title = 'Page not found';
            $message = 'This page could not be found.';
        }

        return $this->renderHtmlPage($title, $message);
    }

    public function renderHtmlPage(string $title = '', string $message = ''): string
    {
        $title = htmlentities($title, ENT_COMPAT|ENT_HTML5, 'utf-8');
        $message = htmlentities($message, ENT_COMPAT|ENT_HTML5, 'utf-8');

        return <<<EOT
<!DOCTYPE html>
<html>
<head>
  <title>$title - My website</title>
  <link rel="stylesheet"
     href="https://cdnjs.cloudflare.com/ajax/libs/mini.css/3.0.1/mini-default.css">
</head>
<body>
  <h1>$title</h1>
  <p>$message</p>
</body>
</html>
EOT;
    }
}

We can do whatever we like in __invoke(). In this case, I have chosen to change the title and message based on the class of the $exception. Slim will show a HttpNotFoundException when the router cannot find a registered route for the request URL, so we can use this to show different text explaining the problem.

We also need to render the HTML itself. This is likely to match the rest of your website's design, so the implementation of renderHtmlPage() is entirely up to you. I'm sure you can do better than I can though!

Register our renderer

We register our renderer, at the point we add the ErrorMiddleare, which in my case is in config/middleware.php:

$displayErrorDetails = (bool)($_ENV['DEBUG'] ?? false);
    $errorMiddleware = $app->addErrorMiddleware($displayErrorDetails, true, true);
    $errorHandler = $errorMiddleware->getDefaultErrorHandler();
    $errorHandler->registerErrorRenderer('text/html', HtmlErrorRenderer::class);

Don't forget to add use App\Error\Renderer\HtmlErrorRenderer; at the top of the file and now the new error renderer will now be used.

This is what our new error page looks like:

Slim4 new error page

Using Whoops for development

When we're developing, it would be useful to have more information about what's gone wrong. The whoops error handler can provide this, so let's add it. Fortunately for us, the hard work has been done already by Zeusis in their zeuxisoo/slim-whoops package, so just install it:

$ composer require "zeuxisoo/slim-whoops"

We can now add the WhoopsMiddleare if we are in debug mode:

if ((bool)($ENV_['DEBUG'] ?? false)) {
        $app->add(new WhoopsMiddleware(['enable' => true]));
    } else {
        $errorMiddleware = $app->addErrorMiddleware(false, true, true);
        $errorHandler    = $errorMiddleware->getDefaultErrorHandler();
        $errorHandler->registerErrorRenderer('text/html', HtmlErrorRenderer::class);
    }

I've triggered it on the DEBUG environment variable, bu too course, you should use whatever method works for you in order to determine when the app is in development mode.

Slim4 whoops error

To sum up

That's it. Changing the error rendering ensure that your error pages look part of your website and it's easy to do so. In addition, the technique can also be used to render custom error payloads for APIs by registering error renderers for the error handler's application/json and application/xml content types.


Viewing all articles
Browse latest Browse all 38

Trending Articles