vendor/api-platform/core/src/Action/ExceptionAction.php line 58

  1. <?php
  2. /*
  3.  * This file is part of the API Platform project.
  4.  *
  5.  * (c) Kévin Dunglas <dunglas@gmail.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. declare(strict_types=1);
  11. namespace ApiPlatform\Action;
  12. use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
  13. use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
  14. use ApiPlatform\Util\ErrorFormatGuesser;
  15. use ApiPlatform\Util\OperationRequestInitiatorTrait;
  16. use ApiPlatform\Util\RequestAttributesExtractor;
  17. use Symfony\Component\Debug\Exception\FlattenException as LegacyFlattenException;
  18. use Symfony\Component\ErrorHandler\Exception\FlattenException;
  19. use Symfony\Component\HttpFoundation\Request;
  20. use Symfony\Component\HttpFoundation\Response;
  21. use Symfony\Component\Serializer\SerializerInterface;
  22. /**
  23.  * Renders a normalized exception for a given {@see FlattenException} or {@see LegacyFlattenException}.
  24.  *
  25.  * @author Baptiste Meyer <baptiste.meyer@gmail.com>
  26.  * @author Kévin Dunglas <dunglas@gmail.com>
  27.  */
  28. final class ExceptionAction
  29. {
  30.     use OperationRequestInitiatorTrait;
  31.     private $serializer;
  32.     private $errorFormats;
  33.     private $exceptionToStatus;
  34.     /**
  35.      * @var ResourceMetadataCollectionFactoryInterface|ResourceMetadataFactoryInterface|null
  36.      */
  37.     private $resourceMetadataFactory;
  38.     /**
  39.      * @param array      $errorFormats            A list of enabled error formats
  40.      * @param array      $exceptionToStatus       A list of exceptions mapped to their HTTP status code
  41.      * @param mixed|null $resourceMetadataFactory
  42.      */
  43.     public function __construct(SerializerInterface $serializer, array $errorFormats, array $exceptionToStatus = [], $resourceMetadataFactory null)
  44.     {
  45.         $this->serializer $serializer;
  46.         $this->errorFormats $errorFormats;
  47.         $this->exceptionToStatus $exceptionToStatus;
  48.         $this->resourceMetadataFactory $resourceMetadataFactory;
  49.         if (null !== $resourceMetadataFactory && !$resourceMetadataFactory instanceof ResourceMetadataCollectionFactoryInterface) {
  50.             trigger_deprecation('api-platform/core''2.7'sprintf('Use "%s" instead of "%s".'ResourceMetadataCollectionFactoryInterface::class, ResourceMetadataFactoryInterface::class));
  51.         } else {
  52.             $this->resourceMetadataCollectionFactory $resourceMetadataFactory;
  53.         }
  54.     }
  55.     /**
  56.      * Converts an exception to a JSON response.
  57.      *
  58.      * @param FlattenException|LegacyFlattenException $exception
  59.      */
  60.     public function __invoke($exceptionRequest $request): Response
  61.     {
  62.         $operation $this->initializeOperation($request);
  63.         $exceptionClass $exception->getClass();
  64.         $statusCode $exception->getStatusCode();
  65.         $exceptionToStatus array_merge(
  66.             $this->exceptionToStatus,
  67.             $operation $operation->getExceptionToStatus() ?? [] : $this->getOperationExceptionToStatus($request)
  68.         );
  69.         foreach ($exceptionToStatus as $class => $status) {
  70.             if (is_a($exceptionClass$classtrue)) {
  71.                 $statusCode $status;
  72.                 break;
  73.             }
  74.         }
  75.         $headers $exception->getHeaders();
  76.         $format ErrorFormatGuesser::guessErrorFormat($request$this->errorFormats);
  77.         $headers['Content-Type'] = sprintf('%s; charset=utf-8'$format['value'][0]);
  78.         $headers['X-Content-Type-Options'] = 'nosniff';
  79.         $headers['X-Frame-Options'] = 'deny';
  80.         return new Response($this->serializer->serialize($exception$format['key'], ['statusCode' => $statusCode]), $statusCode$headers);
  81.     }
  82.     private function getOperationExceptionToStatus(Request $request): array
  83.     {
  84.         // TODO: remove legacy layer in 3.0
  85.         if ($request->attributes->has('_api_exception_to_status')) {
  86.             return $request->attributes->get('_api_exception_to_status');
  87.         }
  88.         $attributes RequestAttributesExtractor::extractAttributes($request);
  89.         if ([] === $attributes || null === $this->resourceMetadataFactory) {
  90.             return [];
  91.         }
  92.         $resourceMetadata $this->resourceMetadataFactory->create($attributes['resource_class']);
  93.         $operationExceptionToStatus $resourceMetadata->getOperationAttribute($attributes'exception_to_status', [], false);
  94.         $resourceExceptionToStatus $resourceMetadata->getAttribute('exception_to_status', []);
  95.         if (!\is_array($operationExceptionToStatus) || !\is_array($resourceExceptionToStatus)) {
  96.             throw new \LogicException('"exception_to_status" attribute should be an array.');
  97.         }
  98.         return array_merge(
  99.             $resourceExceptionToStatus,
  100.             $operationExceptionToStatus
  101.         );
  102.     }
  103. }
  104. class_alias(ExceptionAction::class, \ApiPlatform\Core\Action\ExceptionAction::class);