Skip to content

Commit 00be27b

Browse files
committed
Merge branch 'fix-exception-handling' into 'master'
Fix response status code in ExceptionListener for Symfony HTTP exceptions See merge request undabot/json-api-symfony!45
2 parents aa20ff6 + 4f60237 commit 00be27b

File tree

3 files changed

+63
-11
lines changed

3 files changed

+63
-11
lines changed

src/Exception/EventSubscriber/ExceptionListener.php

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

77
use Symfony\Component\Debug\Exception\FlattenException;
88
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
9+
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
910
use Undabot\JsonApi\Definition\Encoding\DocumentToPhpArrayEncoderInterface;
1011
use Undabot\JsonApi\Definition\Exception\Request\ClientGeneratedIdIsNotAllowedException;
1112
use Undabot\JsonApi\Definition\Exception\Request\RequestException;
@@ -74,6 +75,18 @@ public function onKernelException(ExceptionEvent $event): void
7475
return;
7576
}
7677

78+
if ($exception instanceof HttpExceptionInterface) {
79+
$errorCollection = new ErrorCollection([
80+
$this->buildError($exception),
81+
]);
82+
$document = new Document(null, $errorCollection);
83+
$data = $this->documentToPhpArrayEncoderInterface->encode($document);
84+
$response = JsonApiHttpResponse::fromSymfonyHttpException($data, $exception);
85+
$event->setResponse($response);
86+
87+
return;
88+
}
89+
7790
if ($exception instanceof \Exception) {
7891
$errorCollection = new ErrorCollection([
7992
$this->buildError($exception),
@@ -87,14 +100,14 @@ public function onKernelException(ExceptionEvent $event): void
87100
}
88101
}
89102

90-
private function buildError(\Exception $exception): Error
103+
private function buildError(\Throwable $exception): Error
91104
{
92105
if (class_exists('\Symfony\Component\ErrorHandler\Exception\FlattenException')) {
93106
/** @var callable $callable */
94-
$callable = ['Symfony\Component\ErrorHandler\Exception\FlattenException', 'create'];
107+
$callable = ['Symfony\Component\ErrorHandler\Exception\FlattenException', 'createFromThrowable'];
95108
$e = \call_user_func($callable, $exception);
96109
} else {
97-
$e = FlattenException::create($exception);
110+
$e = FlattenException::createFromThrowable($exception);
98111
}
99112

100113
return new Error(

src/Http/Model/Response/JsonApiHttpResponse.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Undabot\SymfonyJsonApi\Http\Model\Response;
66

77
use Symfony\Component\HttpFoundation\Response;
8+
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
89

910
class JsonApiHttpResponse extends Response
1011
{
@@ -110,4 +111,22 @@ public static function serverError(array $data): self
110111
]
111112
);
112113
}
114+
115+
/**
116+
* @param array<string, mixed> $data
117+
*
118+
* @throws \JsonException
119+
*/
120+
public static function fromSymfonyHttpException(array $data, HttpExceptionInterface $exception): self
121+
{
122+
$content = json_encode($data, JSON_THROW_ON_ERROR);
123+
124+
return new self(
125+
$content ?: null,
126+
$exception->getStatusCode(),
127+
[
128+
'Content-Type' => self::CONTENT_TYPE,
129+
]
130+
);
131+
}
113132
}

tests/Unit/Exception/EventSubscriber/ExceptionListenerTest.php

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PHPUnit\Framework\TestCase;
99
use Symfony\Component\HttpFoundation\Request;
1010
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
11+
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
1112
use Symfony\Component\HttpKernel\KernelInterface;
1213
use Symfony\Component\Validator\ConstraintViolationList;
1314
use Undabot\JsonApi\Definition\Encoding\DocumentToPhpArrayEncoderInterface;
@@ -79,7 +80,7 @@ public function exceptionProvider(): \Generator
7980

8081
public function testOnKernelExceptionWillSetCorrectEventResponseGivenGivenExceptionIsSupportedAndEventHaveThrowableMethod(): void
8182
{
82-
$event = new DummyThrowableEvent(
83+
$event = new ExceptionEvent(
8384
$this->createMock(KernelInterface::class),
8485
$this->createMock(Request::class),
8586
KernelInterface::MASTER_REQUEST,
@@ -93,22 +94,41 @@ public function testOnKernelExceptionWillSetCorrectEventResponseGivenGivenExcept
9394

9495
$this->exceptionListener->onKernelException($event);
9596
static::assertEquals(
96-
$event->getResponse(),
9797
new JsonApiHttpResponse(
9898
json_encode($data),
9999
500,
100100
[
101101
'Content-Type' => 'application/vnd.api+json',
102102
],
103-
)
103+
),
104+
$event->getResponse()
104105
);
105106
}
106-
}
107107

108-
class DummyThrowableEvent extends ExceptionEvent
109-
{
110-
public function getThrowable(): \Throwable
108+
public function testOnKernelExceptionWillSetCorrectEventResponseGivenGivenSymfonyHttpException(): void
111109
{
112-
return new \LogicException();
110+
$event = new ExceptionEvent(
111+
$this->createMock(KernelInterface::class),
112+
$this->createMock(Request::class),
113+
KernelInterface::MASTER_REQUEST,
114+
$e = new AccessDeniedHttpException()
115+
);
116+
$data = [];
117+
$this->documentToPhpArrayEncoderInterfaceMock
118+
->expects(static::once())
119+
->method('encode')
120+
->willReturn($data);
121+
122+
$this->exceptionListener->onKernelException($event);
123+
static::assertEquals(
124+
new JsonApiHttpResponse(
125+
json_encode($data),
126+
$e->getStatusCode(),
127+
[
128+
'Content-Type' => 'application/vnd.api+json',
129+
],
130+
),
131+
$event->getResponse()
132+
);
113133
}
114134
}

0 commit comments

Comments
 (0)