Error Handling Middleware

Things go wrong. You can’t predict errors, but you can anticipate them. Each Slim Framework application has an error handler that receives all uncaught PHP exceptions. This error handler also receives the current HTTP request and response objects, too. The error handler must prepare and return an appropriate Response object to be returned to the HTTP client.

Usage

  1. <?php
  2. use Slim\Factory\AppFactory;
  3. require __DIR__ . '/../vendor/autoload.php';
  4. $app = AppFactory::create();
  5. /*
  6. * The routing middleware should be added earlier than the ErrorMiddleware
  7. * Otherwise exceptions thrown from it will not be handled by the middleware
  8. */
  9. $app->addRoutingMiddleware();
  10. /*
  11. *
  12. * @param bool $displayErrorDetails -> Should be set to false in production
  13. * @param bool $logErrors -> Parameter is passed to the default ErrorHandler
  14. * @param bool $logErrorDetails -> Display error details in error log
  15. * which can be replaced by a callable of your choice.
  16. *
  17. * Note: This middleware should be added last. It will not handle any exceptions/errors
  18. * for middleware added after it.
  19. */
  20. $errorMiddleware = $app->addErrorMiddleware(true, true, true);
  21. // ...
  22. $app->run();

Adding Custom Error Handlers

You can now map custom handlers for any type of Exception or Throwable.

  1. <?php
  2. use Psr\Http\Message\ServerRequestInterface;
  3. use Slim\Factory\AppFactory;
  4. use Slim\Psr7\Response;
  5. require __DIR__ . '/../vendor/autoload.php';
  6. $app = AppFactory::create();
  7. // Add Routing Middleware
  8. $app->addRoutingMiddleware();
  9. // Define Custom Error Handler
  10. $customErrorHandler = function (
  11. ServerRequestInterface $request,
  12. Throwable $exception,
  13. bool $displayErrorDetails,
  14. bool $logErrors,
  15. bool $logErrorDetails
  16. ) use ($app) {
  17. $payload = ['error' => $exception->getMessage()];
  18. $response = $app->getResponseFactory()->createResponse();
  19. $response->getBody()->write(
  20. json_encode($payload, JSON_UNESCAPED_UNICODE)
  21. );
  22. return $response;
  23. };
  24. // Add Error Middleware
  25. $errorMiddleware = $app->addErrorMiddleware(true, true, true);
  26. $errorMiddleware->setDefaultErrorHandler($customErrorHandler);
  27. // ...
  28. $app->run();

Error Logging

If you would like to pipe in custom error logging to the default ErrorHandler that ships with Slim you can simply extend it and stub the logError() method.

  1. <?php
  2. namespace MyApp\Handlers;
  3. use Slim\Handlers\ErrorHandler;
  4. class MyErrorHandler extends ErrorHandler
  5. {
  6. protected function logError(string $error): void
  7. {
  8. // Insert custom error logging function.
  9. }
  10. }
  1. <?php
  2. use MyApp\Handlers\MyErrorHandler;
  3. use Slim\Factory\AppFactory;
  4. require __DIR__ . '/../vendor/autoload.php';
  5. $app = AppFactory::create();
  6. // Add Routing Middleware
  7. $app->addRoutingMiddleware();
  8. // Instantiate Your Custom Error Handler
  9. $myErrorHandler = new MyErrorHandler($app->getCallableResolver(), $app->getResponseFactory());
  10. // Add Error Middleware
  11. $errorMiddleware = $app->addErrorMiddleware(true, true, true);
  12. $errorMiddleware->setDefaultErrorHandler($myErrorHandler);
  13. // ...
  14. $app->run();

Error Handling/Rendering

The rendering is finally decoupled from the handling.It will still detect the content-type and render things appropriately with the help of ErrorRenderers.The core ErrorHandler extends the AbstractErrorHandler class which has been completely refactored.By default it will call the appropriate ErrorRenderer for the supported content types. The coreErrorHandler defines renderers for the following content types:

  • application/json
  • application/xml and text/xml
  • text/html
  • text/plainFor any content type you can register your own error renderer. So first define a new error rendererthat implements \Slim\Interfaces\ErrorRendererInterface.
  1. <?php
  2. use Slim\Interfaces\ErrorRendererInterface;
  3. class MyCustomErrorRenderer implements ErrorRendererInterface
  4. {
  5. public function __invoke(Throwable $exception, bool $displayErrorDetails): string
  6. {
  7. return 'My awesome format';
  8. }
  9. }

And then register that error renderer in the core error handler. In the example below wewill register the renderer to be used for text/html content types.

  1. <?php
  2. use MyApp\Handlers\MyErrorHandler;
  3. use Slim\Factory\AppFactory;
  4. require __DIR__ . '/../vendor/autoload.php';
  5. $app = AppFactory::create();
  6. // Add Routing Middleware
  7. $app->addRoutingMiddleware();
  8. // Add Error Middleware
  9. $errorMiddleware = $app->addErrorMiddleware(true, true, true);
  10. // Get the default error handler and register my custom error renderer.
  11. $errorHandler = $errorMiddleware->getDefaultErrorHandler();
  12. $errorHandler->registerErrorRenderer('text/html', MyCustomErrorRenderer::class);
  13. // ...
  14. $app->run();

Force a specific content type for error rendering

By default, the error handler tries to detect the error renderer using the Accept header of therequest. If you need to force the error handler to use a specific error renderer you can write the following.

  1. $errorHandler->forceContentType('application/json');

New HTTP Exceptions

We have added named HTTP exceptions within the application. These exceptions work nicely with the native renderers. They can each have a description and title attribute as well to provide a bit more insight when the native HTML renderer is invoked.

The base class HttpSpecializedException extends Exception and comes with the following sub classes:

  • HttpBadRequestException
  • HttpForbiddenException
  • HttpInternalServerErrorException
  • HttpNotAllowedException
  • HttpNotFoundException
  • HttpNotImplementedException
  • HttpUnauthorizedExceptionYou can extend the HttpSpecializedException class if they need any other response codes that we decide not to provide with the base repository. Example if you wanted a 504 gateway timeout exception that behaves like the native ones you would do the following:
  1. class HttpForbiddenException extends HttpException
  2. {
  3. protected $code = 504;
  4. protected $message = 'Gateway Timeout.';
  5. protected $title = '504 Gateway Timeout';
  6. protected $description = 'Timed out before receiving response from the upstream server.';
  7. }