错误中间件

事情出了问题。您无法预测错误,但可以预见错误。每个 Slim 框架应用程序都有一个错误处理程序,用于接收所有未捕获的 PHP 异常。此错误处理程序也接收当前的 HTTP 请求和响应对象。错误处理程序必须准备并返回一个适当的 Response 对象以返回给 HTTP 客户端。

用法

<?php
use Slim\Factory\AppFactory;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

/**
 * The routing middleware should be added earlier than the ErrorMiddleware
 * Otherwise exceptions thrown from it will not be handled by the middleware
 */
$app->addRoutingMiddleware();

/**
 * Add Error Middleware
 *
 * @param bool                  $displayErrorDetails -> Should be set to false in production
 * @param bool                  $logErrors -> Parameter is passed to the default ErrorHandler
 * @param bool                  $logErrorDetails -> Display error details in error log
 * @param LoggerInterface|null  $logger -> Optional PSR-3 Logger
 *
 * Note: This middleware should be added last. It will not handle any exceptions/errors
 * for middleware added after it.
 */
$errorMiddleware = $app->addErrorMiddleware(true, true, true);

// ...

$app->run();

添加自定义错误处理程序

您现在可以为任何类型的 Exception 或 Throwable 映射自定义处理程序。

<?php
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
use Slim\Factory\AppFactory;
use Slim\Psr7\Response;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

// Add Routing Middleware
$app->addRoutingMiddleware();

// Define Custom Error Handler
$customErrorHandler = function (
    ServerRequestInterface $request,
    Throwable $exception,
    bool $displayErrorDetails,
    bool $logErrors,
    bool $logErrorDetails,
    ?LoggerInterface $logger = null
) use ($app) {
    $logger->error($exception->getMessage());

    $payload = ['error' => $exception->getMessage()];

    $response = $app->getResponseFactory()->createResponse();
    $response->getBody()->write(
        json_encode($payload, JSON_UNESCAPED_UNICODE)
    );

    return $response;
};

// Add Error Middleware
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
$errorMiddleware->setDefaultErrorHandler($customErrorHandler);

// ...

$app->run();

错误记录

如果你想将自定义错误记录通过管道ErrorHandler传输到 Slim 附带的默认值,有两种方法可以做到。

使用第一种方法,您可以简单地扩展ErrorHandler和存根该logError()方法。

<?php
namespace MyApp\Handlers;

use Slim\Handlers\ErrorHandler;

class MyErrorHandler extends ErrorHandler
{
    protected function logError(string $error): void
    {
        // Insert custom error logging function.
    }
}
<?php
use MyApp\Handlers\MyErrorHandler;
use Slim\Factory\AppFactory;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

// Add Routing Middleware
$app->addRoutingMiddleware();

// Instantiate Your Custom Error Handler
$myErrorHandler = new MyErrorHandler($app->getCallableResolver(), $app->getResponseFactory());

// Add Error Middleware
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
$errorMiddleware->setDefaultErrorHandler($myErrorHandler);

// ...

$app->run();

使用第二种方法,您可以提供符合 PSR-3 标准的记录器,例如来自流行的 Monolog库的记录器。

<?php
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use MyApp\Handlers\MyErrorHandler;
use Slim\Factory\AppFactory;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

// Add Routing Middleware
$app->addRoutingMiddleware();

// Monolog Example
$logger = new Logger('app');
$streamHandler = new StreamHandler(__DIR__ . '/var/log', 100);
$logger->pushHandler($streamHandler);

// Add Error Middleware with Logger
$errorMiddleware = $app->addErrorMiddleware(true, true, true, $logger);

// ...

$app->run();

错误处理/渲染

渲染最终与处理分离。它仍然会检测内容类型并在 的帮助下适当地呈现内容ErrorRenderers核心ErrorHandler扩展了AbstractErrorHandler已经完全重构的类。默认情况下,它将ErrorRenderer为支持的内容类型调用适当的。核心 ErrorHandler为以下内容类型定义渲染器:

  • application/json
  • application/xmltext/xml
  • text/html
  • text/plain

对于任何内容类型,您都可以注册自己的错误渲染器。所以首先定义一个新的错误渲染器来实现\Slim\Interfaces\ErrorRendererInterface.

<?php
use Slim\Interfaces\ErrorRendererInterface;
use Throwable;

class MyCustomErrorRenderer implements ErrorRendererInterface
{
    public function __invoke(Throwable $exception, bool $displayErrorDetails): string
    {
        return 'My awesome format';
    }
}

然后在核心错误处理程序中注册该错误渲染器。在下面的示例中,我们将注册用于text/html内容类型的渲染器。

<?php
use MyApp\Handlers\MyErrorHandler;
use Slim\Factory\AppFactory;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

// Add Routing Middleware
$app->addRoutingMiddleware();

// Add Error Middleware
$errorMiddleware = $app->addErrorMiddleware(true, true, true);

// Get the default error handler and register my custom error renderer.
$errorHandler = $errorMiddleware->getDefaultErrorHandler();
$errorHandler->registerErrorRenderer('text/html', MyCustomErrorRenderer::class);

// ...

$app->run();

强制特定内容类型进行错误呈现

Accept默认情况下,错误处理程序会尝试使用请求的标头来检测错误渲染器。如果您需要强制错误处理程序使用特定的错误渲染器,您可以编写以下内容。

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

新的 HTTP 异常

我们在应用程序中添加了命名的 HTTP 异常。这些异常与本机渲染器配合得很好。它们每个都可以有一个descriptiontitle属性,以便在调用本机 HTML 渲染器时提供更多的洞察力。

基类HttpSpecializedException扩展Exception并附带以下子类:

  • HttpBadRequestException
  • HttpForbiddenException
  • HttpInternalServerErrorException
  • HttpMethodNotAllowedException
  • HttpNotFoundException
  • HttpNotImplementedException
  • HttpUnauthorizedException

HttpSpecializedException如果他们需要我们决定不与基本存储库一起提供的任何其他响应代码,您可以扩展该类。例如,如果您想要一个 504 网关超时异常,其行为类似于本机异常,您将执行以下操作:

class HttpForbiddenException extends HttpSpecializedException
{
    protected $code = 504;
    protected $message = 'Gateway Timeout.';
    protected $title = '504 Gateway Timeout';
    protected $description = 'Timed out before receiving response from the upstream server.';
}