Skip to content

Commit

Permalink
Merge branch '2.x'
Browse files Browse the repository at this point in the history
* 2.x:
  add listener mapping exceptions to status codes
  • Loading branch information
xabbuh committed Apr 23, 2020
2 parents fb3acbc + 92afde2 commit 9285881
Show file tree
Hide file tree
Showing 43 changed files with 1,284 additions and 81 deletions.
4 changes: 4 additions & 0 deletions Controller/ExceptionController.php
Expand Up @@ -11,6 +11,8 @@

namespace FOS\RestBundle\Controller;

@trigger_error(sprintf('The %s\ExceptionController class is deprecated since FOSRestBundle 2.8.', __NAMESPACE__), E_USER_DEPRECATED);

use FOS\RestBundle\Util\ExceptionValueMap;
use FOS\RestBundle\View\View;
use FOS\RestBundle\View\ViewHandlerInterface;
Expand All @@ -20,6 +22,8 @@

/**
* Custom ExceptionController that uses the view layer and supports HTTP response status code mapping.
*
* @deprecated since 2.8
*/
final class ExceptionController
{
Expand Down
5 changes: 4 additions & 1 deletion DependencyInjection/Compiler/JMSHandlersPass.php
Expand Up @@ -33,8 +33,11 @@ public function process(ContainerBuilder $container): void
return;
}

if ($container->hasDefinition('fos_rest.serializer.exception_normalizer.jms')) {
$container->removeDefinition('fos_rest.serializer.exception_normalizer.jms');
}

$container->removeDefinition('fos_rest.serializer.handler_registry');
$container->removeDefinition('fos_rest.serializer.exception_normalizer.jms');
$container->getParameterBag()->remove('jms_serializer.form_error_handler.class');
}
}
48 changes: 46 additions & 2 deletions DependencyInjection/Configuration.php
Expand Up @@ -426,8 +426,52 @@ private function addExceptionSection(ArrayNodeDefinition $rootNode): void
->addDefaultsIfNotSet()
->canBeEnabled()
->children()
->scalarNode('exception_controller')->defaultValue('fos_rest.exception.controller::showAction')->end()
->scalarNode('service')->defaultNull()->end()
->booleanNode('map_exception_codes')
->defaultFalse()
->info('Enables an event listener that maps exception codes to response status codes based on the map configured with the "fos_rest.exception.codes" option.')
->end()
->booleanNode('exception_listener')
->defaultValue(function () {
@trigger_error('Enabling the "fos_rest.exception.exception_listener" option is deprecated since FOSRestBundle 2.8.', E_USER_DEPRECATED);

return true;
})
->beforeNormalization()
->ifTrue()
->then(function ($v) {
@trigger_error('Enabling the "fos_rest.exception.exception_listener" option is deprecated since FOSRestBundle 2.8.', E_USER_DEPRECATED);

return $v;
})
->end()
->end()
->scalarNode('exception_controller')
->defaultNull()
->setDeprecated('The "%path%.%node%" option is deprecated since FOSRestBundle 2.8.')
->end()
->booleanNode('serialize_exceptions')
->defaultValue(function () {
@trigger_error('Enabling the "fos_rest.exception.serialize_exceptions" option is deprecated since FOSRestBundle 2.8.', E_USER_DEPRECATED);

return true;
})
->beforeNormalization()
->ifTrue()
->then(function ($v) {
@trigger_error('Enabling the "fos_rest.exception.serialize_exceptions" option is deprecated since FOSRestBundle 2.8.', E_USER_DEPRECATED);

return $v;
})
->end()
->end()
->enumNode('flatten_exception_format')
->defaultValue('legacy')
->values(['legacy', 'rfc7807'])
->end()
->scalarNode('service')
->defaultNull()
->setDeprecated('The "%path%.%node%" option is deprecated since FOSRestBundle 2.8.')
->end()
->arrayNode('codes')
->useAttributeAsKey('name')
->beforeNormalization()
Expand Down
72 changes: 50 additions & 22 deletions DependencyInjection/FOSRestExtension.php
Expand Up @@ -11,6 +11,7 @@

namespace FOS\RestBundle\DependencyInjection;

use FOS\RestBundle\EventListener\ResponseStatusCodeListener;
use FOS\RestBundle\View\ViewHandler;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Alias;
Expand Down Expand Up @@ -310,41 +311,68 @@ private function loadException(array $config, XmlFileLoader $loader, ContainerBu
if ($config['exception']['enabled']) {
$loader->load('exception_listener.xml');

if (!empty($config['exception']['service'])) {
$service = $container->getDefinition('fos_rest.exception_listener');
$service->clearTag('kernel.event_subscriber');
if ($config['exception']['map_exception_codes']) {
$container->register('fos_rest.exception.response_status_code_listener', ResponseStatusCodeListener::class)
->setArguments([
new Reference('fos_rest.exception.codes_map'),
])
->addTag('kernel.event_subscriber');
}

$controller = $config['exception']['exception_controller'] ?? null;
if ($config['exception']['exception_listener']) {
if (!empty($config['exception']['service'])) {
$service = $container->getDefinition('fos_rest.exception_listener');
$service->clearTag('kernel.event_subscriber');
}

if (class_exists(ErrorListener::class)) {
$container->register('fos_rest.error_listener', ErrorListener::class)
->setArguments([
$controller,
new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE),
'%kernel.debug%',
])
->addTag('monolog.logger', ['channel' => 'request']);
$controller = $config['exception']['exception_controller'] ?? null;

if (class_exists(ErrorListener::class)) {
$container->register('fos_rest.error_listener', ErrorListener::class)
->setArguments([
$controller,
new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE),
'%kernel.debug%',
])
->addTag('monolog.logger', ['channel' => 'request']);
} else {
$container->register('fos_rest.error_listener', LegacyHttpKernelExceptionListener::class)
->setArguments([
$controller,
new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE),
])
->addTag('monolog.logger', ['channel' => 'request']);
}

$container->getDefinition('fos_rest.exception.controller')
->replaceArgument(2, $config['exception']['debug']);
} else {
$container->register('fos_rest.error_listener', LegacyHttpKernelExceptionListener::class)
->setArguments([
$controller,
new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE),
])
->addTag('monolog.logger', ['channel' => 'request']);
$container->removeDefinition('fos_rest.exception_listener');
}

$container->getDefinition('fos_rest.exception.codes_map')
->replaceArgument(0, $config['exception']['codes']);
$container->getDefinition('fos_rest.exception.messages_map')
->replaceArgument(0, $config['exception']['messages']);

$container->getDefinition('fos_rest.exception.controller')
->replaceArgument(2, $config['exception']['debug']);
$container->getDefinition('fos_rest.serializer.exception_normalizer.jms')
$container->getDefinition('fos_rest.serializer.flatten_exception_handler')
->replaceArgument(1, $config['exception']['debug']);
$container->getDefinition('fos_rest.serializer.exception_normalizer.symfony')
$container->getDefinition('fos_rest.serializer.flatten_exception_handler')
->replaceArgument(2, 'rfc7807' === $config['exception']['flatten_exception_format']);
$container->getDefinition('fos_rest.serializer.flatten_exception_normalizer')
->replaceArgument(1, $config['exception']['debug']);
$container->getDefinition('fos_rest.serializer.flatten_exception_normalizer')
->replaceArgument(2, 'rfc7807' === $config['exception']['flatten_exception_format']);

if ($config['exception']['serialize_exceptions']) {
$container->getDefinition('fos_rest.serializer.exception_normalizer.jms')
->replaceArgument(1, $config['exception']['debug']);
$container->getDefinition('fos_rest.serializer.exception_normalizer.symfony')
->replaceArgument(1, $config['exception']['debug']);
} else {
$container->removeDefinition('fos_rest.serializer.exception_normalizer.jms');
$container->removeDefinition('fos_rest.serializer.exception_normalizer.symfony');
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion EventListener/ExceptionListener.php
Expand Up @@ -11,6 +11,8 @@

namespace FOS\RestBundle\EventListener;

@trigger_error(sprintf('The %s\ExceptionListener class is deprecated since FOSRestBundle 2.8.', __NAMESPACE__), E_USER_DEPRECATED);

use FOS\RestBundle\FOSRestBundle;
use Symfony\Component\Debug\Exception\FlattenException as LegacyFlattenException;
use Symfony\Component\ErrorHandler\Exception\FlattenException;
Expand All @@ -27,7 +29,7 @@
*
* @author Ener-Getick <egetick@gmail.com>
*
* @internal
* @deprecated since 2.8
*/
class ExceptionListener implements EventSubscriberInterface
{
Expand Down
85 changes: 85 additions & 0 deletions EventListener/ResponseStatusCodeListener.php
@@ -0,0 +1,85 @@
<?php

/*
* This file is part of the FOSRestBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FOS\RestBundle\EventListener;

use FOS\RestBundle\FOSRestBundle;
use FOS\RestBundle\Util\ExceptionValueMap;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

/**
* @author Christian Flothmann <christian.flothmann@sensiolabs.de>
*/
class ResponseStatusCodeListener implements EventSubscriberInterface
{
private $exceptionValueMap;
private $responseStatusCode;

public function __construct(ExceptionValueMap $exceptionValueMap)
{
$this->exceptionValueMap = $exceptionValueMap;
}

public static function getSubscribedEvents(): array
{
return [
KernelEvents::EXCEPTION => 'getResponseStatusCodeFromThrowable',
KernelEvents::RESPONSE => 'setResponseStatusCode',
];
}

/**
* @param ExceptionEvent $event
*/
public function getResponseStatusCodeFromThrowable($event): void
{
if (!$event->isMasterRequest()) {
return;
}

$request = $event->getRequest();

if (!$request->attributes->get(FOSRestBundle::ZONE_ATTRIBUTE, true)) {
return;
}

if (method_exists($event, 'getThrowable')) {
$throwable = $event->getThrowable();
} else {
$throwable = $event->getException();
}

$statusCode = $this->exceptionValueMap->resolveThrowable($throwable);

if (is_int($statusCode)) {
$this->responseStatusCode = $statusCode;
}
}

/**
* @param ResponseEvent $event
*/
public function setResponseStatusCode($event): void
{
if (!$event->isMasterRequest()) {
return;
}

if (null !== $this->responseStatusCode) {
$event->getResponse()->setStatusCode($this->responseStatusCode);

$this->responseStatusCode = null;
}
}
}
18 changes: 18 additions & 0 deletions Resources/config/exception_listener.xml
Expand Up @@ -9,12 +9,14 @@
<tag name="kernel.event_subscriber" />
<argument type="service" id="fos_rest.error_listener" />
<argument type="service" id="event_dispatcher" />
<deprecated>The "%service_id%" service is deprecated since FOSRestBundle 2.8.</deprecated>
</service>

<service id="fos_rest.exception.controller" class="FOS\RestBundle\Controller\ExceptionController" public="true">
<argument type="service" id="fos_rest.view_handler" />
<argument type="service" id="fos_rest.exception.codes_map" /> <!-- exception codes -->
<argument /><!-- show exception -->
<deprecated>The "%service_id%" service is deprecated since FOSRestBundle 2.8.</deprecated>
</service>

<service id="fos_rest.exception.codes_map" class="FOS\RestBundle\Util\ExceptionValueMap" public="false">
Expand All @@ -29,12 +31,28 @@
<argument type="service" id="fos_rest.exception.messages_map" /> <!-- exception messages -->
<argument /><!-- show exception message -->
<tag name="jms_serializer.subscribing_handler" />
<deprecated>The "%service_id%" service is deprecated since FOSRestBundle 2.8.</deprecated>
</service>

<service id="fos_rest.serializer.exception_normalizer.symfony" class="FOS\RestBundle\Serializer\Normalizer\ExceptionNormalizer" public="false">
<argument type="service" id="fos_rest.exception.messages_map" /> <!-- exception messages -->
<argument /><!-- show exception message -->
<tag name="serializer.normalizer" priority="-10" />
<deprecated>The "%service_id%" service is deprecated since FOSRestBundle 2.8.</deprecated>
</service>

<service id="fos_rest.serializer.flatten_exception_handler" class="FOS\RestBundle\Serializer\Normalizer\FlattenExceptionHandler" public="false">
<argument type="service" id="fos_rest.exception.messages_map" /> <!-- exception messages -->
<argument /><!-- show exception message -->
<argument /><!-- render according to RFC 7807 -->
<tag name="jms_serializer.subscribing_handler" />
</service>

<service id="fos_rest.serializer.flatten_exception_normalizer" class="FOS\RestBundle\Serializer\Normalizer\FlattenExceptionNormalizer" public="false">
<argument type="service" id="fos_rest.exception.messages_map" /> <!-- exception messages -->
<argument /><!-- show exception message -->
<argument /><!-- render according to RFC 7807 -->
<tag name="serializer.normalizer" priority="-10" />
</service>
</services>
</container>
4 changes: 3 additions & 1 deletion Serializer/Normalizer/ExceptionHandler.php
Expand Up @@ -11,14 +11,16 @@

namespace FOS\RestBundle\Serializer\Normalizer;

@trigger_error(sprintf('The %s\ExceptionHandler class is deprecated since FOSRestBundle 2.8.', __NAMESPACE__), E_USER_DEPRECATED);

use JMS\Serializer\Context;
use JMS\Serializer\GraphNavigatorInterface;
use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\JsonSerializationVisitor;
use JMS\Serializer\XmlSerializationVisitor;

/**
* @internal
* @deprecated since 2.8
*/
class ExceptionHandler extends AbstractExceptionNormalizer implements SubscribingHandlerInterface
{
Expand Down
4 changes: 3 additions & 1 deletion Serializer/Normalizer/ExceptionNormalizer.php
Expand Up @@ -11,14 +11,16 @@

namespace FOS\RestBundle\Serializer\Normalizer;

@trigger_error(sprintf('The %s\ExceptionNormalizer class is deprecated since FOSRestBundle 2.8.', __NAMESPACE__), E_USER_DEPRECATED);

use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
* Normalizes Exception instances.
*
* @author Ener-Getick <egetick@gmail.com>
*
* @internal
* @deprecated since 2.8
*/
class ExceptionNormalizer extends AbstractExceptionNormalizer implements NormalizerInterface
{
Expand Down

0 comments on commit 9285881

Please sign in to comment.