vendor/api-platform/core/src/EventListener/DeserializeListener.php line 69

Open in your IDE?
  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\Core\EventListener;
  12. use ApiPlatform\Core\Api\FormatMatcher;
  13. use ApiPlatform\Core\Api\FormatsProviderInterface;
  14. use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
  15. use ApiPlatform\Core\Metadata\Resource\ToggleableOperationAttributeTrait;
  16. use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
  17. use ApiPlatform\Core\Util\RequestAttributesExtractor;
  18. use Symfony\Component\HttpFoundation\Request;
  19. use Symfony\Component\HttpKernel\Event\RequestEvent;
  20. use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
  21. use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
  22. use Symfony\Component\Serializer\SerializerInterface;
  23. /**
  24.  * Updates the entity retrieved by the data provider with data contained in the request body.
  25.  *
  26.  * @author Kévin Dunglas <dunglas@gmail.com>
  27.  */
  28. final class DeserializeListener
  29. {
  30.     use ToggleableOperationAttributeTrait;
  31.     public const OPERATION_ATTRIBUTE_KEY 'deserialize';
  32.     private $serializer;
  33.     private $serializerContextBuilder;
  34.     private $formats;
  35.     private $formatsProvider;
  36.     /**
  37.      * @param ResourceMetadataFactoryInterface|FormatsProviderInterface|array $resourceMetadataFactory
  38.      */
  39.     public function __construct(SerializerInterface $serializerSerializerContextBuilderInterface $serializerContextBuilder$resourceMetadataFactoryResourceMetadataFactoryInterface $legacyResourceMetadataFactory null)
  40.     {
  41.         $this->serializer $serializer;
  42.         $this->serializerContextBuilder $serializerContextBuilder;
  43.         $this->resourceMetadataFactory $resourceMetadataFactory instanceof ResourceMetadataFactoryInterface $resourceMetadataFactory $legacyResourceMetadataFactory;
  44.         if (!$resourceMetadataFactory instanceof ResourceMetadataFactoryInterface) {
  45.             @trigger_error(sprintf('Passing an array or an instance of "%s" as 3rd parameter of the constructor of "%s" is deprecated since API Platform 2.5, pass an instance of "%s" instead'FormatsProviderInterface::class, __CLASS__ResourceMetadataFactoryInterface::class), \E_USER_DEPRECATED);
  46.         }
  47.         if (\is_array($resourceMetadataFactory)) {
  48.             $this->formats $resourceMetadataFactory;
  49.         } elseif ($resourceMetadataFactory instanceof FormatsProviderInterface) {
  50.             $this->formatsProvider $resourceMetadataFactory;
  51.         }
  52.     }
  53.     /**
  54.      * Deserializes the data sent in the requested format.
  55.      *
  56.      * @throws UnsupportedMediaTypeHttpException
  57.      */
  58.     public function onKernelRequest(RequestEvent $event): void
  59.     {
  60.         $request $event->getRequest();
  61.         $method $request->getMethod();
  62.         if (
  63.             'DELETE' === $method
  64.             || $request->isMethodSafe()
  65.             || !($attributes RequestAttributesExtractor::extractAttributes($request))
  66.             || !$attributes['receive']
  67.             || $this->isOperationAttributeDisabled($attributesself::OPERATION_ATTRIBUTE_KEY)
  68.         ) {
  69.             return;
  70.         }
  71.         $context $this->serializerContextBuilder->createFromRequest($requestfalse$attributes);
  72.         // BC check to be removed in 3.0
  73.         if ($this->resourceMetadataFactory) {
  74.             $formats $this
  75.                 ->resourceMetadataFactory
  76.                 ->create($attributes['resource_class'])
  77.                 ->getOperationAttribute($attributes'input_formats', [], true);
  78.         } elseif ($this->formatsProvider instanceof FormatsProviderInterface) {
  79.             $formats $this->formatsProvider->getFormatsFromAttributes($attributes);
  80.         } else {
  81.             $formats $this->formats;
  82.         }
  83.         $format $this->getFormat($request$formats);
  84.         $data $request->attributes->get('data');
  85.         if (null !== $data) {
  86.             $context[AbstractNormalizer::OBJECT_TO_POPULATE] = $data;
  87.         }
  88.         $request->attributes->set(
  89.             'data',
  90.             $this->serializer->deserialize($request->getContent(), $context['resource_class'], $format$context)
  91.         );
  92.     }
  93.     /**
  94.      * Extracts the format from the Content-Type header and check that it is supported.
  95.      *
  96.      * @throws UnsupportedMediaTypeHttpException
  97.      */
  98.     private function getFormat(Request $request, array $formats): string
  99.     {
  100.         /**
  101.          * @var string|null
  102.          */
  103.         $contentType $request->headers->get('CONTENT_TYPE');
  104.         if (null === $contentType) {
  105.             throw new UnsupportedMediaTypeHttpException('The "Content-Type" header must exist.');
  106.         }
  107.         $formatMatcher = new FormatMatcher($formats);
  108.         $format $formatMatcher->getFormat($contentType);
  109.         if (null === $format) {
  110.             $supportedMimeTypes = [];
  111.             foreach ($formats as $mimeTypes) {
  112.                 foreach ($mimeTypes as $mimeType) {
  113.                     $supportedMimeTypes[] = $mimeType;
  114.                 }
  115.             }
  116.             throw new UnsupportedMediaTypeHttpException(sprintf('The content-type "%s" is not supported. Supported MIME types are "%s".'$contentTypeimplode('", "'$supportedMimeTypes)));
  117.         }
  118.         return $format;
  119.     }
  120. }