vendor/friendsofsymfony/rest-bundle/EventListener/BodyListener.php line 57

  1. <?php
  2. /*
  3.  * This file is part of the FOSRestBundle package.
  4.  *
  5.  * (c) FriendsOfSymfony <http://friendsofsymfony.github.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. namespace FOS\RestBundle\EventListener;
  11. use FOS\RestBundle\Decoder\DecoderProviderInterface;
  12. use FOS\RestBundle\FOSRestBundle;
  13. use FOS\RestBundle\Normalizer\ArrayNormalizerInterface;
  14. use FOS\RestBundle\Normalizer\Exception\NormalizationException;
  15. use Symfony\Component\HttpFoundation\InputBag;
  16. use Symfony\Component\HttpFoundation\ParameterBag;
  17. use Symfony\Component\HttpFoundation\Request;
  18. use Symfony\Component\HttpKernel\Event\RequestEvent;
  19. use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
  20. use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
  21. /**
  22.  * This listener handles Request body decoding.
  23.  *
  24.  * @author Lukas Kahwe Smith <smith@pooteeweet.org>
  25.  *
  26.  * @internal
  27.  */
  28. class BodyListener
  29. {
  30.     private $decoderProvider;
  31.     private $throwExceptionOnUnsupportedContentType;
  32.     private $defaultFormat;
  33.     private $arrayNormalizer;
  34.     private $normalizeForms;
  35.     public function __construct(
  36.         DecoderProviderInterface $decoderProvider,
  37.         bool $throwExceptionOnUnsupportedContentType false,
  38.         ArrayNormalizerInterface $arrayNormalizer null,
  39.         bool $normalizeForms false
  40.     ) {
  41.         $this->decoderProvider $decoderProvider;
  42.         $this->throwExceptionOnUnsupportedContentType $throwExceptionOnUnsupportedContentType;
  43.         $this->arrayNormalizer $arrayNormalizer;
  44.         $this->normalizeForms $normalizeForms;
  45.     }
  46.     public function setDefaultFormat(?string $defaultFormat): void
  47.     {
  48.         $this->defaultFormat $defaultFormat;
  49.     }
  50.     public function onKernelRequest(RequestEvent $event): void
  51.     {
  52.         $request $event->getRequest();
  53.         if (!$request->attributes->get(FOSRestBundle::ZONE_ATTRIBUTEtrue)) {
  54.             return;
  55.         }
  56.         $method $request->getMethod();
  57.         $contentType $request->headers->get('Content-Type');
  58.         $normalizeRequest $this->normalizeForms && $this->isFormRequest($request);
  59.         if ($this->isDecodeable($request)) {
  60.             $format null === $contentType
  61.                 $request->getRequestFormat()
  62.                 : $request->getFormat($contentType);
  63.             $format $format ?: $this->defaultFormat;
  64.             $content $request->getContent();
  65.             if (null === $format || !$this->decoderProvider->supports($format)) {
  66.                 if ($this->throwExceptionOnUnsupportedContentType
  67.                     && $this->isNotAnEmptyDeleteRequestWithNoSetContentType($method$content$contentType)
  68.                 ) {
  69.                     throw new UnsupportedMediaTypeHttpException("Request body format '$format' not supported");
  70.                 }
  71.                 return;
  72.             }
  73.             if (!empty($content)) {
  74.                 $decoder $this->decoderProvider->getDecoder($format);
  75.                 $data $decoder->decode($content);
  76.                 if (is_array($data)) {
  77.                     if (class_exists(InputBag::class)) {
  78.                         $request->request = new InputBag($data);
  79.                     } else {
  80.                         $request->request = new ParameterBag($data);
  81.                     }
  82.                     $normalizeRequest true;
  83.                 } else {
  84.                     throw new BadRequestHttpException('Invalid '.$format.' message received');
  85.                 }
  86.             }
  87.         }
  88.         if (null !== $this->arrayNormalizer && $normalizeRequest) {
  89.             $data $request->request->all();
  90.             try {
  91.                 $data $this->arrayNormalizer->normalize($data);
  92.             } catch (NormalizationException $e) {
  93.                 throw new BadRequestHttpException($e->getMessage());
  94.             }
  95.             if (class_exists(InputBag::class)) {
  96.                 $request->request = new InputBag($data);
  97.             } else {
  98.                 $request->request = new ParameterBag($data);
  99.             }
  100.         }
  101.     }
  102.     private function isNotAnEmptyDeleteRequestWithNoSetContentType(string $method$content, ?string $contentType): bool
  103.     {
  104.         return false === ('DELETE' === $method && empty($content) && empty($contentType));
  105.     }
  106.     private function isDecodeable(Request $request): bool
  107.     {
  108.         if (!in_array($request->getMethod(), ['POST''PUT''PATCH''DELETE'])) {
  109.             return false;
  110.         }
  111.         return !$this->isFormRequest($request);
  112.     }
  113.     private function isFormRequest(Request $request): bool
  114.     {
  115.         $contentTypeParts explode(';'$request->headers->get('Content-Type'''));
  116.         if (isset($contentTypeParts[0])) {
  117.             return in_array($contentTypeParts[0], ['multipart/form-data''application/x-www-form-urlencoded']);
  118.         }
  119.         return false;
  120.     }
  121. }