Skip to content

错误处理

事情总会出错。你无法预测错误,但可以预期它们的出现。每个 Slim Framework 应用程序都有一个错误处理器,它接收所有未捕获的 PHP 异常。此错误处理器还会接收当前的 HTTP 请求和响应对象。错误处理器必须准备并返回适当的响应对象以返回给 HTTP 客户端。

使用方法

<?php
use Slim\Factory\AppFactory;

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

$app = AppFactory::create();

/**
 * 路由中间件应该在ErrorMiddleware之前添加
 * 否则从路由抛出的异常将不会被中间件处理
 */
$app->addRoutingMiddleware();

/**
 * 添加 Error Middleware
 *
 * @param bool                  $displayErrorDetails -> 在生产环境中应该设置为 false
 * @param bool                  $logErrors -> 传递给默认的 ErrorHandler 的参数
 * @param bool                  $logErrorDetails -> 在错误日志中显示错误详细信息
 * @param LoggerInterface|null  $logger -> 可选的 PSR-3 日志记录器  
 *
 * 注意:这个中间件应该最后添加。它将不会处理添加在它之后的任何中间件引发的异常/错误。
 */
$errorMiddleware = $app->addErrorMiddleware(true, true, true);

// ...

$app->run();

添加自定义错误处理器

你现在可以为任何类型的异常或 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();

// 添加路由中间件
$app->addRoutingMiddleware();

// 定义自定义错误处理器
$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;
};

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

// ...

$app->run();

错误日志记录

如果你想将自定义错误日志记录到 Slim 默认的 ErrorHandler 中,有两种方法可以实现。

使用第一种方法,你可以简单地继承 ErrorHandler 并存根化 logError() 方法。

<?php
namespace MyApp\Handlers;

use Slim\Handlers\ErrorHandler;

class MyErrorHandler extends ErrorHandler
{
    protected function logError(string $error): void
    {
        // 插入自定义的错误日志记录函数
    }
}
<?php
use MyApp\Handlers\MyErrorHandler;
use Slim\Factory\AppFactory;

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

$app = AppFactory::create();

// 添加路由中间件
$app->addRoutingMiddleware();

// 实例化你的自定义错误处理器
$myErrorHandler = new MyErrorHandler($app->getCallableResolver(), $app->getResponseFactory());

// 添加 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();

// 添加路由中间件
$app->addRoutingMiddleware();

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

// 添加含有日志记录器的 Error Middleware
$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();

// 添加路由中间件
$app->addRoutingMiddleware();

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

// 获取默认的错误处理器并注册我自定义的错误渲染器。
$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 HttpGatewayTimeoutException 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.';
}