Search code examples
phpslimslim-4

Replacement for notFoundHandler setting


I'm migrating from Slim/3 to Slim/4. I've found or figured out replacements for all the features I was using that have been removed, except 404 Not Found Handler (part of the now gone App::$settings):

Slim App::$settings have been removed, multiple middleware have been implemented to replace the functionality from each individual settings.

Is there a middleware for notFoundHandler? If there isn't, how can I implement it?

Mine used to look like this:

use Slim\Container;
$config = new Container(); 
$config['notFoundHandler'] = function (Container $c) {
    return function (Request $request, Response $response) use ($c): Response {
        $page = new Alvaro\Pages\Error($c);
        return $page->notFound404($request, $response);
    };
};

Solution

  • According to Slim 4 documentation on error handling

    Each Slim Framework application has an error handler that receives all uncaught PHP exceptions

    You can set a custom error handler to handle each type of exceptions thrown. A list of predefined exception classes is available on same page.

    Here is a very basic example of how to register a closure as an error handler, to handle only HttpNotFoundException exceptions. You can also put the handler in a class that extends Slim\Handlers\ErrorHandler. Also, I did not actually use your Alvaro\Pages\Error to generate the response, but changing it should be straightforward:

    <?php
    
    require '../vendor/autoload.php';
    
    $app = Slim\Factory\AppFactory::create();
    
    // Define Custom Error Handler
    $customErrorHandler = function (
        Psr\Http\Message\ServerRequestInterface $request,
        \Throwable $exception,
        bool $displayErrorDetails,
        bool $logErrors,
        bool $logErrorDetails
    ) use ($app) {
        $response = $app->getResponseFactory()->createResponse();
        // seems the followin can be replaced by your custom response
        // $page = new Alvaro\Pages\Error($c);
        // return $page->notFound404($request, $response);
        $response->getBody()->write('not found');
        return $response->withStatus(404);
    };
    
    // Add Error Middleware
    $errorMiddleware = $app->addErrorMiddleware(true, true, true);
    // Register the handler to handle only  HttpNotFoundException
    // Changing the first parameter registers the error handler for other types of exceptions
    $errorMiddleware->setErrorHandler(Slim\Exception\HttpNotFoundException::class, $customErrorHandler);
    
    
    $app->get('/', function ($request, $response) {
        $response->getBody()->write('Hello Slim 4');
        return $response;
    });
    
    $app->run();
    

    Another approach is to create a generic error handler and register it as the default handler, and inside that handler, decide what response should be sent based on type of exception that is thrown. Something like:

    $customErrorHandler = function (
        Psr\Http\Message\ServerRequestInterface $request,
        \Throwable $exception,
        bool $displayErrorDetails,
        bool $logErrors,
        bool $logErrorDetails
    ) use ($app) {
        $response = $app->getResponseFactory()->createResponse();
    
            if ($exception instanceof HttpNotFoundException) {
                $message = 'not found';
                $code = 404;
            } elseif ($exception instanceof HttpMethodNotAllowedException) {
                $message = 'not allowed';
                $code = 403;
            }
            // ...other status codes, messages, or generally other responses for other types of exceptions
    
        $response->getBody()->write($message);
        return $response->withStatus($code);
    };
    

    Then you can set this as the default error handler:

    $errorMiddleware = $app->addErrorMiddleware(true, true, true);
    $errorMiddleware->setDefaultErrorHandler($customErrorHandler);