vendor/nijens/openapi-bundle/src/EventListener/JsonRequestBodyValidationSubscriber.php line 78

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of the OpenapiBundle package.
  5.  *
  6.  * (c) Niels Nijens <nijens.niels@gmail.com>
  7.  *
  8.  * For the full copyright and license information, please view the LICENSE
  9.  * file that was distributed with this source code.
  10.  */
  11. namespace Nijens\OpenapiBundle\EventListener;
  12. use JsonSchema\Validator;
  13. use Nijens\OpenapiBundle\Exception\BadJsonRequestHttpException;
  14. use Nijens\OpenapiBundle\Exception\InvalidRequestHttpException;
  15. use Nijens\OpenapiBundle\Json\JsonPointer;
  16. use Nijens\OpenapiBundle\Json\SchemaLoaderInterface;
  17. use Nijens\OpenapiBundle\Routing\RouteContext;
  18. use Nijens\OpenapiBundle\Validation\ValidationContext;
  19. use Seld\JsonLint\JsonParser;
  20. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  21. use Symfony\Component\HttpFoundation\HeaderUtils;
  22. use Symfony\Component\HttpKernel\Event\RequestEvent;
  23. use Symfony\Component\HttpKernel\KernelEvents;
  24. /**
  25.  * Validates a JSON request body for routes loaded through the OpenAPI specification.
  26.  *
  27.  * @deprecated since 1.5, to be removed in 2.0. Use the new request validation instead.
  28.  *
  29.  * @author Niels Nijens <nijens.niels@gmail.com>
  30.  */
  31. class JsonRequestBodyValidationSubscriber implements EventSubscriberInterface
  32. {
  33.     /**
  34.      * @var JsonParser
  35.      */
  36.     private $jsonParser;
  37.     /**
  38.      * @var SchemaLoaderInterface
  39.      */
  40.     private $schemaLoader;
  41.     /**
  42.      * @var Validator
  43.      */
  44.     private $jsonValidator;
  45.     /**
  46.      * {@inheritdoc}
  47.      */
  48.     public static function getSubscribedEvents(): array
  49.     {
  50.         return [
  51.             KernelEvents::REQUEST => [
  52.                 ['validateRequestBody'28],
  53.             ],
  54.         ];
  55.     }
  56.     /**
  57.      * Constructs a new JsonRequestBodyValidationSubscriber instance.
  58.      */
  59.     public function __construct(JsonParser $jsonParserSchemaLoaderInterface $schemaLoaderValidator $jsonValidator)
  60.     {
  61.         $this->jsonParser $jsonParser;
  62.         $this->schemaLoader $schemaLoader;
  63.         $this->jsonValidator $jsonValidator;
  64.     }
  65.     /**
  66.      * Validates the body of a request to an OpenAPI specification route. Throws an exception when validation fails.
  67.      */
  68.     public function validateRequestBody(RequestEvent $event): void
  69.     {
  70.         $request $event->getRequest();
  71.         $requestContentType current(HeaderUtils::split($request->headers->get('Content-Type'''), ';'));
  72.         $routeOptions $event->getRequest()->attributes->get(RouteContext::REQUEST_ATTRIBUTE);
  73.         // Not an openAPI route.
  74.         if (isset($routeOptions[RouteContext::RESOURCE]) === false) {
  75.             return;
  76.         }
  77.         // No need for validation.
  78.         if (isset($routeOptions[RouteContext::JSON_REQUEST_VALIDATION_POINTER]) === false) {
  79.             return;
  80.         }
  81.         if ($requestContentType !== 'application/json') {
  82.             throw new BadJsonRequestHttpException("The request content-type must be 'application/json'.");
  83.         }
  84.         $requestBody $request->getContent();
  85.         $decodedJsonRequestBody $this->validateJsonRequestBody($requestBody);
  86.         $this->validateJsonAgainstSchema(
  87.             $routeOptions[RouteContext::RESOURCE],
  88.             $routeOptions[RouteContext::JSON_REQUEST_VALIDATION_POINTER],
  89.             $decodedJsonRequestBody
  90.         );
  91.         $event->getRequest()->attributes->set(
  92.             ValidationContext::REQUEST_ATTRIBUTE,
  93.             [
  94.                 ValidationContext::VALIDATED => true,
  95.                 ValidationContext::REQUEST_BODY => json_encode($decodedJsonRequestBody),
  96.             ]
  97.         );
  98.     }
  99.     /**
  100.      * Validates if the request body is valid JSON.
  101.      *
  102.      * @return mixed
  103.      */
  104.     private function validateJsonRequestBody(string $requestBody)
  105.     {
  106.         $decodedJsonRequestBody json_decode($requestBody);
  107.         if ($decodedJsonRequestBody !== null || $requestBody === 'null') {
  108.             return $decodedJsonRequestBody;
  109.         }
  110.         $exception $this->jsonParser->lint($requestBody);
  111.         throw new BadJsonRequestHttpException('The request body should be valid JSON.'$exception);
  112.     }
  113.     /**
  114.      * Validates the JSON request body against the JSON Schema within the OpenAPI specification.
  115.      *
  116.      * @param mixed $decodedJsonRequestBody
  117.      */
  118.     private function validateJsonAgainstSchema(string $openApiResourcestring $openApiValidationPointer, &$decodedJsonRequestBody): void
  119.     {
  120.         $schema $this->schemaLoader->load($openApiResource);
  121.         $jsonPointer = new JsonPointer($schema);
  122.         $jsonSchema $jsonPointer->get($openApiValidationPointer);
  123.         $this->jsonValidator->validate($decodedJsonRequestBody$jsonSchema);
  124.         if ($this->jsonValidator->isValid() === false) {
  125.             $validationErrors $this->jsonValidator->getErrors();
  126.             $this->jsonValidator->reset();
  127.             $this->throwInvalidRequestException($validationErrors);
  128.         }
  129.     }
  130.     private function throwInvalidRequestException(array $errors): void
  131.     {
  132.         $exception = new InvalidRequestHttpException('Validation of JSON request body failed.');
  133.         $exception->setErrors($errors);
  134.         throw $exception;
  135.     }
  136. }