Skip to content

Commit 668a665

Browse files
authored
Merge pull request #412 from dotkernel/issue-410
Issue #410: Fixed error handling
2 parents d1329db + 7ce4c5d commit 668a665

File tree

4 files changed

+65
-57
lines changed

4 files changed

+65
-57
lines changed

config/pipeline.php

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
use Api\App\Middleware\AuthorizationMiddleware;
88
use Api\App\Middleware\ContentNegotiationMiddleware;
99
use Api\App\Middleware\DeprecationMiddleware;
10-
use Api\App\Middleware\ResponseMiddleware;
11-
use Dot\ErrorHandler\ErrorHandlerInterface;
1210
use Dot\ResponseHeader\Middleware\ResponseHeaderMiddleware;
1311
use Mezzio\Application;
1412
use Mezzio\Cors\Middleware\CorsMiddleware;
@@ -24,9 +22,9 @@
2422
use Mezzio\Router\Middleware\RouteMiddleware;
2523

2624
return function (Application $app): void {
27-
// The error handler should be the first (most outer) middleware to catch
28-
// all Exceptions.
29-
$app->pipe(ErrorHandlerInterface::class);
25+
// This middleware must be the outest layer because - on error occurrence - it will:
26+
// - call \Dot\ErrorHandler\ErrorHandlerInterface::class
27+
// - return ProblemDetails response
3028
$app->pipe(ProblemDetailsMiddleware::class);
3129

3230
$app->pipe(BodyParamsMiddleware::class);
@@ -38,15 +36,14 @@
3836
// - pre-conditions
3937
// - modifications to outgoing responses
4038
//
41-
// Piped Middleware may be either callables or service names. Middleware may
42-
// also be passed as an array; each item in the array must resolve to
43-
// middleware eventually (i.e., callable or service name).
39+
// Piped Middleware may be either callables or service names.
40+
// Middleware may also be passed as an array; each item in the array must resolve to middleware eventually
41+
// (i.e., callable or service name).
4442
//
45-
// Middleware can be attached to specific paths, allowing you to mix and match
46-
// applications under a common domain. The handlers in each middleware
47-
// attached this way will see a URI with the matched path segment removed.
43+
// Middleware can be attached to specific paths, allowing you to mix and match applications under a common domain.
44+
// The handlers in each middleware attached this way will see a URI with the matched path segment removed.
4845
//
49-
// i.e., path of "/api/member/profile" only passes "/member/profile" to $apiMiddleware
46+
// For example, the path of "/api/member/profile" only passes "/member/profile" to $apiMiddleware
5047
// - $app->pipe('/api', $apiMiddleware);
5148
// - $app->pipe('/docs', $apiDocMiddleware);
5249
// - $app->pipe('/files', $filesMiddleware);
@@ -83,8 +80,6 @@
8380
// - route-based validation
8481
// - etc.
8582

86-
$app->pipe(ResponseMiddleware::class);
87-
8883
// Register the dispatch middleware in the middleware pipeline
8984
$app->pipe(DispatchMiddleware::class);
9085
// At this point, if no Response is returned by any middleware, the

src/App/src/ConfigProvider.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66

77
use Api\App\Command\TokenGenerateCommand;
88
use Api\App\Factory\HandlerDelegatorFactory;
9+
use Api\App\Factory\ProblemDetailsDelegatorFactory;
910
use Api\App\Handler\GetIndexResourceHandler;
1011
use Api\App\Handler\PostErrorReportResourceHandler;
1112
use Api\App\Middleware\AuthenticationMiddleware;
1213
use Api\App\Middleware\AuthorizationMiddleware;
1314
use Api\App\Middleware\ContentNegotiationMiddleware;
1415
use Api\App\Middleware\DeprecationMiddleware;
1516
use Api\App\Middleware\ErrorReportPermissionMiddleware;
16-
use Api\App\Middleware\ResponseMiddleware;
1717
use Api\App\Service\ErrorReportService;
1818
use Api\App\Service\ErrorReportServiceInterface;
1919
use Dot\DependencyInjection\Factory\AttributedServiceFactory;
@@ -23,6 +23,7 @@
2323
use Mezzio\Authentication\OAuth2\OAuth2Adapter;
2424
use Mezzio\Hal\Metadata\RouteBasedCollectionMetadata;
2525
use Mezzio\Hal\Metadata\RouteBasedResourceMetadata;
26+
use Mezzio\ProblemDetails\ProblemDetailsMiddleware;
2627
use Mezzio\Template\TemplateRendererInterface;
2728
use Mezzio\Twig\TwigEnvironmentFactory;
2829
use Mezzio\Twig\TwigExtension;
@@ -47,14 +48,14 @@ private function getDependencies(): array
4748
Application::class => [RoutesDelegator::class],
4849
GetIndexResourceHandler::class => [HandlerDelegatorFactory::class],
4950
PostErrorReportResourceHandler::class => [HandlerDelegatorFactory::class],
51+
ProblemDetailsMiddleware::class => [ProblemDetailsDelegatorFactory::class],
5052
],
5153
'factories' => [
5254
AuthenticationMiddleware::class => AttributedServiceFactory::class,
5355
AuthorizationMiddleware::class => AttributedServiceFactory::class,
5456
ContentNegotiationMiddleware::class => AttributedServiceFactory::class,
5557
DeprecationMiddleware::class => AttributedServiceFactory::class,
5658
ErrorReportPermissionMiddleware::class => AttributedServiceFactory::class,
57-
ResponseMiddleware::class => AttributedServiceFactory::class,
5859
GetIndexResourceHandler::class => AttributedServiceFactory::class,
5960
PostErrorReportResourceHandler::class => AttributedServiceFactory::class,
6061
ErrorReportService::class => AttributedServiceFactory::class,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Api\App\Factory;
6+
7+
use Api\App\Exception\RuntimeException;
8+
use Core\App\Message;
9+
use Dot\ErrorHandler\ErrorHandlerInterface;
10+
use Dot\ErrorHandler\LogErrorHandler;
11+
use Mezzio\ProblemDetails\ProblemDetailsMiddleware;
12+
use Psr\Container\ContainerExceptionInterface;
13+
use Psr\Container\ContainerInterface;
14+
use Psr\Container\NotFoundExceptionInterface;
15+
use Psr\Http\Message\RequestInterface;
16+
use Psr\Http\Message\ServerRequestInterface;
17+
use Throwable;
18+
19+
use function assert;
20+
21+
class ProblemDetailsDelegatorFactory
22+
{
23+
/**
24+
* @param class-string $name
25+
* @throws ContainerExceptionInterface
26+
* @throws NotFoundExceptionInterface
27+
* @throws RuntimeException
28+
*/
29+
public function __invoke(
30+
ContainerInterface $container,
31+
string $name,
32+
callable $callback
33+
): ProblemDetailsMiddleware {
34+
if (! $container->has(ErrorHandlerInterface::class)) {
35+
throw RuntimeException::create(Message::serviceNotFound(ErrorHandlerInterface::class));
36+
}
37+
38+
$problemDetailsMiddleware = $callback();
39+
assert($problemDetailsMiddleware instanceof ProblemDetailsMiddleware);
40+
41+
$errorHandler = $container->get(ErrorHandlerInterface::class);
42+
assert($errorHandler instanceof LogErrorHandler);
43+
44+
$listener = function (Throwable $throwable, RequestInterface $request) use ($errorHandler) {
45+
assert($request instanceof ServerRequestInterface);
46+
$errorHandler->handleThrowable($throwable, $request);
47+
};
48+
49+
$problemDetailsMiddleware->attachListener($listener);
50+
51+
return $problemDetailsMiddleware;
52+
}
53+
}

src/App/src/Middleware/ResponseMiddleware.php

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)