Skip to content

Commit 1e6d1f1

Browse files
committed
Merge branch 'ogizanagi-stop_swallow_exceptions' into 1.0
2 parents b9f64a3 + a4029bd commit 1e6d1f1

File tree

5 files changed

+87
-43
lines changed

5 files changed

+87
-43
lines changed

Controller/ExceptionController.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the DunglasApiBundle package.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Dunglas\ApiBundle\Controller;
13+
14+
use Dunglas\ApiBundle\Exception\DeserializationException;
15+
use Dunglas\ApiBundle\JsonLd\Response;
16+
use Symfony\Component\Debug\Exception\FlattenException;
17+
use Symfony\Component\HttpFoundation\Request;
18+
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
19+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
20+
21+
/**
22+
* Render a normalized exception for a given {@see \Symfony\Component\Debug\Exception\FlattenException}.
23+
*
24+
* @author Baptiste Meyer <[email protected]>
25+
*/
26+
class ExceptionController
27+
{
28+
/**
29+
* @var NormalizerInterface
30+
*/
31+
private $normalizer;
32+
33+
/**
34+
* @param NormalizerInterface $normalizer
35+
*/
36+
public function __construct(NormalizerInterface $normalizer)
37+
{
38+
$this->normalizer = $normalizer;
39+
}
40+
41+
/**
42+
* Converts a {@see \Symfony\Component\Debug\Exception\FlattenException}
43+
* to a {@see \Dunglas\ApiBundle\JsonLd\Response}.
44+
*
45+
* @param Request $request
46+
* @param FlattenException $exception
47+
* @param DebugLoggerInterface|null $logger
48+
*
49+
* @return Response
50+
*/
51+
public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null)
52+
{
53+
if (
54+
DeserializationException::class === $exception->getClass() ||
55+
is_subclass_of($exception->getClass(), DeserializationException::class)
56+
) {
57+
$exception->setStatusCode(Response::HTTP_BAD_REQUEST);
58+
}
59+
60+
return new Response(
61+
$this->normalizer->normalize($exception, 'hydra-error'),
62+
$exception->getStatusCode(),
63+
$exception->getHeaders()
64+
);
65+
}
66+
}

Hydra/EventListener/RequestExceptionListener.php

Lines changed: 5 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,60 +11,27 @@
1111

1212
namespace Dunglas\ApiBundle\Hydra\EventListener;
1313

14-
use Dunglas\ApiBundle\Exception\DeserializationException;
15-
use Dunglas\ApiBundle\JsonLd\Response;
1614
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
17-
use Symfony\Component\HttpKernel\Exception\HttpException;
18-
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
15+
use Symfony\Component\HttpKernel\EventListener\ExceptionListener;
1916

2017
/**
2118
* Handle requests errors.
2219
*
2320
* @author Samuel ROZE <[email protected]>
2421
* @author Kévin Dunglas <[email protected]>
2522
*/
26-
class RequestExceptionListener
23+
class RequestExceptionListener extends ExceptionListener
2724
{
28-
/**
29-
* @var NormalizerInterface
30-
*/
31-
private $normalizer;
32-
33-
public function __construct(NormalizerInterface $normalizer)
34-
{
35-
$this->normalizer = $normalizer;
36-
}
37-
3825
/**
3926
* @param GetResponseForExceptionEvent $event
4027
*/
4128
public function onKernelException(GetResponseForExceptionEvent $event)
4229
{
43-
if (!$event->isMasterRequest()) {
30+
// Normalize exceptions with hydra errors only for resources
31+
if (!$event->getRequest()->attributes->has('_resource')) {
4432
return;
4533
}
4634

47-
$request = $event->getRequest();
48-
$exception = $event->getException();
49-
50-
if ($exception instanceof HttpException) {
51-
$status = $exception->getStatusCode();
52-
$headers = $exception->getHeaders();
53-
} elseif ($exception instanceof DeserializationException) {
54-
$status = Response::HTTP_BAD_REQUEST;
55-
$headers = [];
56-
} else {
57-
$status = Response::HTTP_INTERNAL_SERVER_ERROR;
58-
$headers = [];
59-
}
60-
61-
// Normalize exceptions with hydra errors only for resources
62-
if ($request->attributes->has('_resource')) {
63-
$event->setResponse(new Response(
64-
$this->normalizer->normalize($exception, 'hydra-error'),
65-
$status,
66-
$headers
67-
));
68-
}
35+
parent::onKernelException($event);
6936
}
7037
}

Hydra/Serializer/ErrorNormalizer.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111

1212
namespace Dunglas\ApiBundle\Hydra\Serializer;
1313

14+
use Symfony\Component\Debug\Exception\FlattenException;
1415
use Symfony\Component\Routing\RouterInterface;
1516
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
1617

1718
/**
18-
* Converts {@see \Exception} to a Hydra error representation.
19+
* Converts {@see \Exception} or {@see \Symfony\Component\Debug\Exception\FlattenException}
20+
* to a Hydra error representation.
1921
*
2022
* @author Kévin Dunglas <[email protected]>
2123
* @author Samuel ROZE <[email protected]>
@@ -46,7 +48,7 @@ public function __construct(RouterInterface $router, $debug)
4648
*/
4749
public function normalize($object, $format = null, array $context = array())
4850
{
49-
if ($object instanceof \Exception) {
51+
if ($object instanceof \Exception || $object instanceof FlattenException) {
5052
$message = $object->getMessage();
5153

5254
if ($this->debug) {
@@ -73,6 +75,6 @@ public function normalize($object, $format = null, array $context = array())
7375
*/
7476
public function supportsNormalization($data, $format = null)
7577
{
76-
return 'hydra-error' === $format && $data instanceof \Exception;
78+
return 'hydra-error' === $format && ($data instanceof \Exception || $data instanceof FlattenException);
7779
}
7880
}

Resources/config/hydra.xml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
<argument>%api.description%</argument>
1515
</service>
1616

17+
<!-- Controllers -->
18+
19+
<service id="api.hydra.controller.exception" class="Dunglas\ApiBundle\Controller\ExceptionController">
20+
<argument type="service" id="api.hydra.normalizer.error" />
21+
</service>
22+
1723
<!-- Event listeners -->
1824

1925
<service id="api.hydra.listener.link_header_response" class="Dunglas\ApiBundle\Hydra\EventListener\LinkHeaderResponseListener">
@@ -23,9 +29,11 @@
2329
</service>
2430

2531
<service id="api.hydra.listener.request_exception" class="Dunglas\ApiBundle\Hydra\EventListener\RequestExceptionListener">
26-
<argument type="service" id="api.hydra.normalizer.error" />
32+
<argument type="string">api.hydra.controller.exception:showAction</argument>
33+
<argument type="service" id="logger" on-invalid="null" />
2734

28-
<tag name="kernel.event_listener" event="kernel.exception" method="onKernelException" />
35+
<tag name="kernel.event_listener" event="kernel.exception" method="onKernelException" priority="-96" />
36+
<tag name="monolog.logger" channel="request" />
2937
</service>
3038

3139
<!-- Serializer -->

Tests/DependencyInjection/DunglasApiExtensionTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ private function getContainerBuilderProphecy($withDoctrine = true)
174174
$containerBuilderProphecy->setDefinition('api.json_ld.normalizer.datetime', $definitionArgument)->shouldBeCalled();
175175
$containerBuilderProphecy->setDefinition('api.json_ld.encoder', $definitionArgument)->shouldBeCalled();
176176
$containerBuilderProphecy->setDefinition('api.hydra.documentation_builder', $definitionArgument)->shouldBeCalled();
177+
$containerBuilderProphecy->setDefinition('api.hydra.controller.exception', $definitionArgument)->shouldBeCalled();
177178
$containerBuilderProphecy->setDefinition('api.hydra.listener.link_header_response', $definitionArgument)->shouldBeCalled();
178179
$containerBuilderProphecy->setDefinition('api.hydra.listener.request_exception', $definitionArgument)->shouldBeCalled();
179180
$containerBuilderProphecy->setDefinition('api.hydra.normalizer.collection', $definitionArgument)->shouldBeCalled();

0 commit comments

Comments
 (0)