错误中间件

事情出错了。你无法预测错误,但你可以预料它们。每个 Slim 框架应用程序都有一个错误处理程序,可以接收所有未捕获的 PHP 异常。此错误处理程序还接收当前 HTTP 请求和响应对象。错误处理程序必须准备并返回一个响应对象,以便返回给 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 Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
use Slim\Factory\AppFactory;

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

$app = AppFactory::create();

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

// Optional: Define custom error logger
$logger = new Logger('error');
$logger->pushHandler(new RotatingFileHandler('error.log'));

// Define Custom Error Handler
$customErrorHandler = function (
    ServerRequestInterface $request,
    Throwable $exception,
    bool $displayErrorDetails,
    bool $logErrors,
    bool $logErrorDetails
) use ($app, $logger) {
    if ($logger) {
        $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, $logger);
$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
    {
        // 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 异常。这些异常与本机渲染器很好用。当调用本机 HTML 渲染器时,它们每个都可以有一个 descriptiontitle 属性,以提供更多详细信息。

基类 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.';
}

要抛出 HTTP 异常,请使用以下代码

use Slim\Exception\HttpNotFoundException;
// ...

throw new HttpNotFoundException($request);

确保在抛出异常时传递 $request 对象。