diff --git a/config/pipeline.php b/config/pipeline.php index eccd760..f537393 100644 --- a/config/pipeline.php +++ b/config/pipeline.php @@ -7,8 +7,6 @@ use Api\App\Middleware\AuthorizationMiddleware; use Api\App\Middleware\ContentNegotiationMiddleware; use Api\App\Middleware\DeprecationMiddleware; -use Api\App\Middleware\ResponseMiddleware; -use Dot\ErrorHandler\ErrorHandlerInterface; use Dot\ResponseHeader\Middleware\ResponseHeaderMiddleware; use Mezzio\Application; use Mezzio\Cors\Middleware\CorsMiddleware; @@ -24,9 +22,9 @@ use Mezzio\Router\Middleware\RouteMiddleware; return function (Application $app): void { - // The error handler should be the first (most outer) middleware to catch - // all Exceptions. - $app->pipe(ErrorHandlerInterface::class); + // This middleware must be the outest layer because - on error occurrence - it will: + // - call \Dot\ErrorHandler\ErrorHandlerInterface::class + // - return ProblemDetails response $app->pipe(ProblemDetailsMiddleware::class); $app->pipe(BodyParamsMiddleware::class); @@ -38,15 +36,14 @@ // - pre-conditions // - modifications to outgoing responses // - // Piped Middleware may be either callables or service names. Middleware may - // also be passed as an array; each item in the array must resolve to - // middleware eventually (i.e., callable or service name). + // Piped Middleware may be either callables or service names. + // Middleware may also be passed as an array; each item in the array must resolve to middleware eventually + // (i.e., callable or service name). // - // Middleware can be attached to specific paths, allowing you to mix and match - // applications under a common domain. The handlers in each middleware - // attached this way will see a URI with the matched path segment removed. + // Middleware can be attached to specific paths, allowing you to mix and match applications under a common domain. + // The handlers in each middleware attached this way will see a URI with the matched path segment removed. // - // i.e., path of "/api/member/profile" only passes "/member/profile" to $apiMiddleware + // For example, the path of "/api/member/profile" only passes "/member/profile" to $apiMiddleware // - $app->pipe('/api', $apiMiddleware); // - $app->pipe('/docs', $apiDocMiddleware); // - $app->pipe('/files', $filesMiddleware); @@ -83,8 +80,6 @@ // - route-based validation // - etc. - $app->pipe(ResponseMiddleware::class); - // Register the dispatch middleware in the middleware pipeline $app->pipe(DispatchMiddleware::class); // At this point, if no Response is returned by any middleware, the diff --git a/src/App/src/ConfigProvider.php b/src/App/src/ConfigProvider.php index 276a791..79f08f6 100644 --- a/src/App/src/ConfigProvider.php +++ b/src/App/src/ConfigProvider.php @@ -6,6 +6,7 @@ use Api\App\Command\TokenGenerateCommand; use Api\App\Factory\HandlerDelegatorFactory; +use Api\App\Factory\ProblemDetailsDelegatorFactory; use Api\App\Handler\GetIndexResourceHandler; use Api\App\Handler\PostErrorReportResourceHandler; use Api\App\Middleware\AuthenticationMiddleware; @@ -13,7 +14,6 @@ use Api\App\Middleware\ContentNegotiationMiddleware; use Api\App\Middleware\DeprecationMiddleware; use Api\App\Middleware\ErrorReportPermissionMiddleware; -use Api\App\Middleware\ResponseMiddleware; use Api\App\Service\ErrorReportService; use Api\App\Service\ErrorReportServiceInterface; use Dot\DependencyInjection\Factory\AttributedServiceFactory; @@ -23,6 +23,7 @@ use Mezzio\Authentication\OAuth2\OAuth2Adapter; use Mezzio\Hal\Metadata\RouteBasedCollectionMetadata; use Mezzio\Hal\Metadata\RouteBasedResourceMetadata; +use Mezzio\ProblemDetails\ProblemDetailsMiddleware; use Mezzio\Template\TemplateRendererInterface; use Mezzio\Twig\TwigEnvironmentFactory; use Mezzio\Twig\TwigExtension; @@ -47,6 +48,7 @@ private function getDependencies(): array Application::class => [RoutesDelegator::class], GetIndexResourceHandler::class => [HandlerDelegatorFactory::class], PostErrorReportResourceHandler::class => [HandlerDelegatorFactory::class], + ProblemDetailsMiddleware::class => [ProblemDetailsDelegatorFactory::class], ], 'factories' => [ AuthenticationMiddleware::class => AttributedServiceFactory::class, @@ -54,7 +56,6 @@ private function getDependencies(): array ContentNegotiationMiddleware::class => AttributedServiceFactory::class, DeprecationMiddleware::class => AttributedServiceFactory::class, ErrorReportPermissionMiddleware::class => AttributedServiceFactory::class, - ResponseMiddleware::class => AttributedServiceFactory::class, GetIndexResourceHandler::class => AttributedServiceFactory::class, PostErrorReportResourceHandler::class => AttributedServiceFactory::class, ErrorReportService::class => AttributedServiceFactory::class, diff --git a/src/App/src/Factory/ProblemDetailsDelegatorFactory.php b/src/App/src/Factory/ProblemDetailsDelegatorFactory.php new file mode 100644 index 0000000..fb0a6c9 --- /dev/null +++ b/src/App/src/Factory/ProblemDetailsDelegatorFactory.php @@ -0,0 +1,53 @@ +has(ErrorHandlerInterface::class)) { + throw RuntimeException::create(Message::serviceNotFound(ErrorHandlerInterface::class)); + } + + $problemDetailsMiddleware = $callback(); + assert($problemDetailsMiddleware instanceof ProblemDetailsMiddleware); + + $errorHandler = $container->get(ErrorHandlerInterface::class); + assert($errorHandler instanceof LogErrorHandler); + + $listener = function (Throwable $throwable, RequestInterface $request) use ($errorHandler) { + assert($request instanceof ServerRequestInterface); + $errorHandler->handleThrowable($throwable, $request); + }; + + $problemDetailsMiddleware->attachListener($listener); + + return $problemDetailsMiddleware; + } +} diff --git a/src/App/src/Middleware/ResponseMiddleware.php b/src/App/src/Middleware/ResponseMiddleware.php deleted file mode 100644 index a68ab47..0000000 --- a/src/App/src/Middleware/ResponseMiddleware.php +++ /dev/null @@ -1,41 +0,0 @@ -handle($request); - } catch (ProblemDetailsExceptionInterface $exception) { - return $this->problemDetailsResponseFactory->createResponseFromThrowable($request, $exception); - } catch (Throwable $exception) { - return $this->problemDetailsResponseFactory->createResponse( - $request, - StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR, - $exception->getMessage() - ); - } - } -}