phperror-handlinghttp-status-code-404slimslim-4

Default route / notFound Error Handling / HttpNotFoundException / Custom Exception Handling in Slim 4 PHP


I want to create a Slim 4 compatible custom error page /JSON reply that is returned, when a non-existing route is request.

Default route (Slim 3)

I've recently upgraded from Slim 3 to Slim 4. With Slim 3, I had a default route that perfectly did the job:

   $app->any('/[{path:.*}]', function (Request $request, Response $response, array $args) {
      // catching any other requests...
      /* ... creating JSON error object and write it to $slimresponse ... */
      return ($slimresponse);
   });

However, when I do this in Slim 4, I get an error

Type: FastRoute\BadRouteException
Code: 0
Message: Cannot register two routes matching "/" for method "GET"

This obviouosly means that Slim recognizes this as double entry for GET /, which is disallowed in Slim 4.

This article also provided no help for Slim 4, unfortunately.

notFound

Furthermore, according to https://www.javaer101.com/en/article/13830039.html, I've tried to add

$app->notFound(function () use ($app) {
    $app->response->setStatus(403);
    echo "Forbidden";
    //output 'access denied', redirect to login page or whatever you want to do.
});

to my routes.php, but it doesn't work:

Call to undefined method Slim\App::notFound()

HttpNotFoundException

Finally, I've also tried to create an error handling method (specifically for HttpNotFoundException, although I don't know how to separate HttpNotImplementedException) https://www.slimframework.com/docs/v4/middleware/error-handling.html, without any success.

Any help is highly appreciated.


Solution

  • I'd posted the question after searching for two or more hours.

    After submitting the question, I've found the answer here. https://odan.github.io/2020/05/27/slim4-error-handling.html#catching-404-not-found-errors

    Here is my new middleware.php:

    use Slim\App;
    use Psr\Http\Message\ServerRequestInterface;
    use Psr\Http\Server\RequestHandlerInterface;
    use Slim\Exception\HttpNotFoundException;
    use Slim\Middleware\ErrorMiddleware;
    use Middlewares\TrailingSlash;
    use Slim\Psr7\Response; 
    
    return function (App $app) {
        // Parse json, form data and xml
        $app->addBodyParsingMiddleware();
    
        // Add the Slim built-in routing middleware
        $app->addRoutingMiddleware();
    
        // always add a trailing slash
        $app->add(new TrailingSlash(true));
    
        // Add BasePathMiddleware
        $app->add(BasePathMiddleware::class);
    
        // HttpNotFoundException
        $app->add(function (ServerRequestInterface $request, 
                            RequestHandlerInterface $handler) {
           try {
              return $handler->handle($request);
           } catch (HttpNotFoundException $httpException) {
               $response = (new Response())->withStatus(404);
               $response->getBody()->write('404 Not found');
    
               return $response;
           }
        });
    
        // Catch exceptions and errors
        $app->add(ErrorMiddleware::class);
    };
    

    For everyone looking for an extension with another exception class, extend the code above by:

           try {
              return $handler->handle($request);
           } catch (HttpNotFoundException $httpException) {
               $response = (new Response())->withStatus(404);
               $response->getBody()->write('404 Not found');
    
               return $response;
           } catch (HttpMethodNotAllowedException $httpException) {
               $response = (new Response())->withStatus(405);
               $response->getBody()->write('405 Method not allowed. Please use one of the following: '.implode(',',$httpException->getAllowedMethods()));
    
               return $response;
           }
    

    Other exception classes can be found on https://github.com/slimphp/Slim/tree/4.x/Slim/Exception

    This is how you can get and display the exception class for developing/debugging purposes:

          } catch (Exception $e) {
             $response = (new Response())->withStatus(500);
             $response->getBody()->write(get_class($e));
    
             return $response;
          }