class JsonApiRequestValidator

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

Subscriber that validates the query parameter names on a JSON:API request.

@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\JsonApiRequestValidator extends \Symfony\Component\EventDispatcher\EventSubscriberInterface

Expanded class hierarchy of JsonApiRequestValidator

See also

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

jsonapi.api.php

1 string reference to 'JsonApiRequestValidator'
jsonapi.services.yml in core/modules/jsonapi/jsonapi.services.yml
core/modules/jsonapi/jsonapi.services.yml
1 service uses JsonApiRequestValidator
jsonapi.custom_query_parameter_names_validator.subscriber in core/modules/jsonapi/jsonapi.services.yml
Drupal\jsonapi\EventSubscriber\JsonApiRequestValidator

File

core/modules/jsonapi/src/EventSubscriber/JsonApiRequestValidator.php, line 24

Namespace

Drupal\jsonapi\EventSubscriber
View source
class JsonApiRequestValidator implements EventSubscriberInterface {
  
  /**
   * Validates JSON:API requests.
   *
   * @param \Symfony\Component\HttpKernel\Event\RequestEvent $event
   *   The event to process.
   */
  public function onRequest(RequestEvent $event) {
    $request = $event->getRequest();
    if ($request->getRequestFormat() !== 'api_json') {
      return;
    }
    $this->validateQueryParams($request);
  }
  
  /**
   * Validates JSON:API requests.
   *
   * @param \Symfony\Component\HttpKernel\Event\ResponseEvent $event
   *   The event to process.
   */
  public function onResponse(ResponseEvent $event) {
    $request = $event->getRequest();
    if ($request->getRequestFormat() !== 'api_json') {
      return;
    }
    // At this point, we've already run validation on the request by checking
    // the query arguments. This means that if the query arguments change, we
    // may need to run this validation again. Therefore, all responses need to
    // vary by url.query_args.
    $response = $event->getResponse();
    if ($response instanceof CacheableResponseInterface) {
      $response->addCacheableDependency((new CacheableMetadata())->addCacheContexts([
        'url.query_args',
      ]));
    }
  }
  
  /**
   * Validates custom (implementation-specific) query parameter names.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The request for which to validate JSON:API query parameters.
   *
   * @return \Drupal\jsonapi\ResourceResponse|null
   *   A JSON:API resource response.
   *
   * @see http://jsonapi.org/format/#query-parameters
   */
  protected function validateQueryParams(Request $request) {
    $invalid_query_params = [];
    foreach (array_keys($request->query
      ->all()) as $query_parameter_name) {
      // Ignore reserved (official) query parameters.
      if (in_array($query_parameter_name, JsonApiSpec::getReservedQueryParameters())) {
        continue;
      }
      if (!JsonApiSpec::isValidCustomQueryParameter($query_parameter_name)) {
        $invalid_query_params[] = $query_parameter_name;
      }
    }
    if (empty($invalid_query_params)) {
      return NULL;
    }
    // Drupal uses the `_format` query parameter for Content-Type negotiation.
    // Using it violates the JSON:API spec. Nudge people nicely in the correct
    // direction. (This is special cased because using it is pretty common.)
    if (in_array('_format', $invalid_query_params, TRUE)) {
      $uri_without_query_string = $request->getSchemeAndHttpHost() . $request->getBaseUrl() . $request->getPathInfo();
      $exception = new CacheableBadRequestHttpException((new CacheableMetadata())->addCacheContexts([
        'url.query_args',
      ]), 'JSON:API does not need that ugly \'_format\' query string! 🤘 Use the URL provided in \'links\' 🙏');
      $exception->setHeaders([
        'Link' => $uri_without_query_string,
      ]);
      throw $exception;
    }
    $message = sprintf('The following query parameters violate the JSON:API spec: \'%s\'.', implode("', '", $invalid_query_params));
    $exception = new CacheableBadRequestHttpException((new CacheableMetadata())->addCacheContexts([
      'url.query_args',
    ]), $message);
    $exception->setHeaders([
      'Link' => 'http://jsonapi.org/format/#query-parameters',
    ]);
    throw $exception;
  }
  
  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() : array {
    $events[KernelEvents::REQUEST][] = [
      'onRequest',
    ];
    // Run before the resource response subscriber (priority 128), so that said
    // subscriber gets the cacheable metadata from this one.
    $events[KernelEvents::RESPONSE][] = [
      'onResponse',
      129,
    ];
    return $events;
  }

}

Members

Title Sort descending Modifiers Object type Summary
JsonApiRequestValidator::getSubscribedEvents public static function
JsonApiRequestValidator::onRequest public function Validates JSON:API requests.
JsonApiRequestValidator::onResponse public function Validates JSON:API requests.
JsonApiRequestValidator::validateQueryParams protected function Validates custom (implementation-specific) query parameter names.

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