Version 4.5.0
— y2sunlight 2020-09-23
関連記事
本章は以下のサイトの Packaged Middleware のセクションを翻訳し若干の補足を加えたのもです。
ルーティングはミドルウェアとして実装されています。まだデフォルトルーターとしてFastRouteを使用していますが、緊密に結合されているわかではありません。別のルーティングライブラリを実装したい場合は、ルーティングインターフェイスの独自の実装を作成することで可能になります。それは、Slimのコンポーネントとルーティングライブラリの間にブリッジを作成する DispatcherInterface
、RouteCollectorInterface
、RouteParserInterface
、および RouteResolverInterface
です。もし determineRouteBeforeAppMiddleware
を使用していた場合は、以前の動作を維持するために、run()
を呼び出す直前に Middleware\RoutingMiddleware
ミドルウェアをアプリケーションに追加する必要があります。
<?php use Slim\Factory\AppFactory; require __DIR__ . '/../vendor/autoload.php'; $app = AppFactory::create(); // Add Routing Middleware $app->addRoutingMiddleware(); // ... $app->run();
物事はうまくいかない。エラーを予言することはできませんが、予想することはできます。それぞれの Slim Framework アプリケーションには、キャッチされなかったすべてのPHP例外を受け取るエラーハンドラーがあります。このエラーハンドラーは、現在のHTTPリクエスト及びレスポンスオブジェクトも受信します。エラーハンドラは、HTTPクライアントに返される適切なResponseオブジェクトを準備して返す必要があります。
<?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(); /** * @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 * which can be replaced by a callable of your choice. * @param \Psr\Log\LoggerInterface $logger -> Optional PSR-3 logger to receive errors * * 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, $logger); // ... $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, $logger); $errorMiddleware->setDefaultErrorHandler($customErrorHandler); // ... $app->run();
Slimに同梱されているデフォルトの ErrorHandler
にカスタムエラーロギングをパイプする場合には、2つの方法があります。
最初の方法では、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();
2番目の方法では、人気のある Monolog ライブラリからのものなど、PSR-3 規約に準拠するロガーを提供できます。
<?php use MyApp\Handlers\MyErrorHandler; use Slim\Factory\AppFactory; require __DIR__ . '/../vendor/autoload.php'; $app = AppFactory::create(); // Add Routing Middleware $app->addRoutingMiddleware(); // Instantiate Logger // $logger = ... // Add Error Middleware with Logger $errorMiddleware = $app->addErrorMiddleware(true, true, true, $logger); // ... $app->run();
レンダリングは最終的にエラー処理から切り離されています。それでもコンテンツタイプを検出し、ErrorRenderers
の助けを借りて適切にレンダリングします。 コアの ErrorHandler
は、完全にリファクタリングされた AbstractErrorHandler
クラスを拡張します。デフォルトでは、サポートされているコンテンツタイプに合わせた ErrorRenderer
が呼び出されます。コアの ErrorHandler
は、次のコンテンツタイプのレンダラーを定義します。
どのコンテンツタイプでも、独自のエラーレンダラーを登録できます。従って、最初に \Slim\Interfaces\ErrorRendererInterface
を実装する新しいエラーレンダラーを定義します。
<?php use Slim\Interfaces\ErrorRendererInterface; 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例外を追加しています。これらの例外は、ネイティブなレンダラーでうまく機能します。ネイティブHTMLレンダラーが呼び出されたときに、もう少し見識を与えるために、それぞれに description
と title
属性を設定することもできます。
基本クラス HttpSpecializedException
は Exception
を拡張し、次のサブクラスが付属しています:
Example if you wanted a 504 gateway timeout exception that behaves like the native ones you would do the following:
ベースリポジトリで提供しないと判断した他のレスポンスコードが必要な場合は、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.'; }
メソッドオーバライド ミドルウエアを使用すると、X-Http-Method-Override
リクエストヘッダーまたはリクエストボディのパラメータ _METHOD
を使用して、着信リクエストのメソッドをオーバーライドできます。このミドルウェアは、ルーティングミドルウェアが追加された後に配置する必要があります。
<?php use Slim\Factory\AppFactory; use Slim\Middleware\MethodOverrideMiddleware; require __DIR__ . '/../vendor/autoload.php'; $app = AppFactory::create(); // Add RoutingMiddleware before we add the MethodOverrideMiddleware so the method is overrode before routing is done $app->addRoutingMiddleware(); // Add MethodOverride middleware $methodOverrideMiddleware = new MethodOverrideMiddleware(); $app->add($methodOverrideMiddleware); // ... $app->run();
出力バッファリング ミドルウェアを使用すると、出力バッファリングの2つのモード(APPEND
(デフォルト)と PREPEND
モード)を切り替えることができます。APPEND
モードでは、既存のレスポンスボディを使用してコンテンツを追加します。PREPEND
モードは、新しいレスポンスボディ オブジェクトを作成し、既存のレスポンスボディからの出力の前にコンテンツを追加します。このミドルウェアは、最後に実行されるようにミドルウェアスタックの中央に配置する必要があります。
<?php use Slim\Factory\AppFactory; use Slim\Middleware\OutputBufferingMiddleware; use Slim\Psr7\Factory\StreamFactory; require __DIR__ . '/../vendor/autoload.php'; $app = AppFactory::create(); $streamFactory = new StreamFactory(); /** * The two modes available are * OutputBufferingMiddleware::APPEND (default mode) - Appends to existing response body * OutputBufferingMiddleware::PREPEND - Creates entirely new response body */ $mode = OutputBufferingMiddleware::APPEND; $outputBufferingMiddleware = new OutputBufferingMiddleware($streamFactory, $mode); $app->add($outputBufferingMiddleware); // ... $app->run();
Web APIでは、JSONまたはXML形式でデータを送信するのが非常に一般的です。箱から出しも、PSR-7の実装はこれらの形式をサポートしていません。リクエストオブジェクトの getBody()
を自分でデコードする必要があります。これは一般的な要件であるため、Slim4はこのタスクを処理するための BodyParsingMiddleware
を提供します。
addErrorMiddlware
を呼び出す前に、ボディ解析ミドルウェアを配置して、スタックが次のようになるようにすることをお勧めします:
<?php use Slim\Factory\AppFactory; require_once __DIR__ . '/../vendor/autoload.php'; $app = AppFactory::create(); // Parse json, form data and xml $app->addBodyParsingMiddleware(); $app->addRoutingMiddleware(); $app->addErrorMiddleware(true, true, true); // ... $app->run();
POSTハンドラーを変更する必要はありません。というのは、BodyParsingMiddleware
は Content-Type
が JSON
メディアタイプに設定されていることを検出し、デコードされたボディをリクエストの parsed bodyプロパティに配置するからです。
ブラウザからウェブサイトにポストされたデータの場合、$request の getParsedBody()
メソッドを使用できます。
これにより、投稿されたデータの配列が返されます。
$app->post('/', function (Request $request, Response $response, $args): Response { $data = $request->getParsedBody(); $html = var_export($data, true); $response->getBody()->write($html); return $response; });
Content-Type
を読み取り、メディアタイプを検出します。application/*
Content Length ミドルウェアは、Content-Length
ヘッダーをレスポンスに自動的に追加します。これは、Slim3 から削除された addContentLengthHeader
の設定を置き換えるためのものです。このミドルウェアは、最後に実行されるように、ミドルウェアスタックの中央に配置する必要があります。
<?php use Slim\Factory\AppFactory; use Slim\Middleware\ContentLengthMiddleware; require __DIR__ . '/../vendor/autoload.php'; $app = AppFactory::create(); $contentLengthMiddleware = new ContentLengthMiddleware(); $app->add($contentLengthMiddleware); // ... $app->run();