class ResourceResponseValidator

Same name in other branches
  1. 9 core/modules/jsonapi/src/EventSubscriber/ResourceResponseValidator.php \Drupal\jsonapi\EventSubscriber\ResourceResponseValidator
  2. 8.9.x core/modules/jsonapi/src/EventSubscriber/ResourceResponseValidator.php \Drupal\jsonapi\EventSubscriber\ResourceResponseValidator
  3. 11.x core/modules/jsonapi/src/EventSubscriber/ResourceResponseValidator.php \Drupal\jsonapi\EventSubscriber\ResourceResponseValidator

Response subscriber that validates a JSON:API response.

This must run after ResourceResponseSubscriber.

@internal JSON:API maintains no PHP API. The API is the HTTP API. This class may change at any time and could break any dependencies on it.

Hierarchy

  • class \Drupal\jsonapi\EventSubscriber\ResourceResponseValidator implements \Symfony\Component\EventDispatcher\EventSubscriberInterface

Expanded class hierarchy of ResourceResponseValidator

See also

https://www.drupal.org/project/drupal/issues/3032787

jsonapi.api.php

\Drupal\rest\EventSubscriber\ResourceResponseSubscriber

1 file declares its use of ResourceResponseValidator
ResourceResponseValidatorTest.php in core/modules/jsonapi/tests/src/Unit/EventSubscriber/ResourceResponseValidatorTest.php
1 string reference to 'ResourceResponseValidator'
jsonapi.services.yml in core/modules/jsonapi/jsonapi.services.yml
core/modules/jsonapi/jsonapi.services.yml
1 service uses ResourceResponseValidator
jsonapi.resource_response_validator.subscriber in core/modules/jsonapi/jsonapi.services.yml
Drupal\jsonapi\EventSubscriber\ResourceResponseValidator

File

core/modules/jsonapi/src/EventSubscriber/ResourceResponseValidator.php, line 28

Namespace

Drupal\jsonapi\EventSubscriber
View source
class ResourceResponseValidator implements EventSubscriberInterface {
    
    /**
     * The JSON:API logger channel.
     *
     * @var \Psr\Log\LoggerInterface
     */
    protected $logger;
    
    /**
     * The schema validator.
     *
     * This property will only be set if the validator library is available.
     *
     * @var \JsonSchema\Validator|null
     */
    protected $validator;
    
    /**
     * The module handler.
     *
     * @var \Drupal\Core\Extension\ModuleHandlerInterface
     */
    protected $moduleHandler;
    
    /**
     * The application's root file path.
     *
     * @var string
     */
    protected $appRoot;
    
    /**
     * Constructs a ResourceResponseValidator object.
     *
     * @param \Psr\Log\LoggerInterface $logger
     *   The JSON:API logger channel.
     * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
     *   The module handler.
     * @param string $app_root
     *   The application's root file path.
     */
    public function __construct(LoggerInterface $logger, ModuleHandlerInterface $module_handler, $app_root) {
        $this->logger = $logger;
        $this->moduleHandler = $module_handler;
        $this->appRoot = $app_root;
    }
    
    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents() : array {
        $events[KernelEvents::RESPONSE][] = [
            'onResponse',
        ];
        return $events;
    }
    
    /**
     * Sets the validator service if available.
     */
    public function setValidator(?Validator $validator = NULL) {
        if ($validator) {
            $this->validator = $validator;
        }
        elseif (class_exists(Validator::class)) {
            $this->validator = new Validator();
        }
    }
    
    /**
     * Validates JSON:API responses.
     *
     * @param \Symfony\Component\HttpKernel\Event\ResponseEvent $event
     *   The event to process.
     */
    public function onResponse(ResponseEvent $event) {
        $response = $event->getResponse();
        if (!str_contains($response->headers
            ->get('Content-Type', ''), 'application/vnd.api+json')) {
            return;
        }
        // Wraps validation in an assert to prevent execution in production.
        assert($this->validateResponse($response, $event->getRequest()), 'A JSON:API response failed validation (see the logs for details). Report this in the Drupal issue queue at https://www.drupal.org/project/issues/drupal');
    }
    
    /**
     * Validates a response against the JSON:API specification.
     *
     * @param \Symfony\Component\HttpFoundation\Response $response
     *   The response to validate.
     * @param \Symfony\Component\HttpFoundation\Request $request
     *   The request containing info about what to validate.
     *
     * @return bool
     *   FALSE if the response failed validation, otherwise TRUE.
     */
    protected function validateResponse(Response $response, Request $request) {
        // If the validator isn't set, then the validation library is not installed.
        if (!$this->validator) {
            return TRUE;
        }
        // Do not use Json::decode here since it coerces the response into an
        // associative array, which creates validation errors.
        $response_data = json_decode($response->getContent());
        if (empty($response_data)) {
            return TRUE;
        }
        $schema_ref = sprintf('file://%s/schema.json', implode('/', [
            $this->appRoot,
            $this->moduleHandler
                ->getModule('jsonapi')
                ->getPath(),
        ]));
        $generic_jsonapi_schema = (object) [
            '$ref' => $schema_ref,
        ];
        return $this->validateSchema($generic_jsonapi_schema, $response_data);
    }
    
    /**
     * Validates a string against a JSON Schema. It logs any possible errors.
     *
     * @param object $schema
     *   The JSON Schema object.
     * @param string $response_data
     *   The JSON string to validate.
     *
     * @return bool
     *   TRUE if the string is a valid instance of the schema. FALSE otherwise.
     */
    protected function validateSchema($schema, $response_data) {
        $this->validator
            ->check($response_data, $schema);
        $is_valid = $this->validator
            ->isValid();
        if (!$is_valid) {
            $this->logger
                ->debug("Response failed validation.\nResponse:\n@data\n\nErrors:\n@errors", [
                '@data' => Json::encode($response_data),
                '@errors' => Json::encode($this->validator
                    ->getErrors()),
            ]);
        }
        return $is_valid;
    }

}

Members

Title Sort descending Modifiers Object type Summary
ResourceResponseValidator::$appRoot protected property The application's root file path.
ResourceResponseValidator::$logger protected property The JSON:API logger channel.
ResourceResponseValidator::$moduleHandler protected property The module handler.
ResourceResponseValidator::$validator protected property The schema validator.
ResourceResponseValidator::getSubscribedEvents public static function
ResourceResponseValidator::onResponse public function Validates JSON:API responses.
ResourceResponseValidator::setValidator public function Sets the validator service if available.
ResourceResponseValidator::validateResponse protected function Validates a response against the JSON:API specification.
ResourceResponseValidator::validateSchema protected function Validates a string against a JSON Schema. It logs any possible errors.
ResourceResponseValidator::__construct public function Constructs a ResourceResponseValidator object.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.