You are here

class HttpExceptionNormalizer in Drupal 9

Same name and namespace in other branches
  1. 8 core/modules/jsonapi/src/Normalizer/HttpExceptionNormalizer.php \Drupal\jsonapi\Normalizer\HttpExceptionNormalizer

Normalizes an HttpException in compliance with the JSON:API specification.

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

Hierarchy

Expanded class hierarchy of HttpExceptionNormalizer

See also

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

jsonapi.api.php

http://jsonapi.org/format/#error-objects

4 files declare their use of HttpExceptionNormalizer
HttpExceptionNormalizerTest.php in core/modules/jsonapi/tests/src/Unit/Normalizer/HttpExceptionNormalizerTest.php
NodeTest.php in core/modules/jsonapi/tests/src/Functional/NodeTest.php
ResourceResponseTestTrait.php in core/modules/jsonapi/tests/src/Functional/ResourceResponseTestTrait.php
ResourceTestBase.php in core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php
1 string reference to 'HttpExceptionNormalizer'
jsonapi.services.yml in core/modules/jsonapi/jsonapi.services.yml
core/modules/jsonapi/jsonapi.services.yml
1 service uses HttpExceptionNormalizer
serializer.normalizer.http_exception.jsonapi in core/modules/jsonapi/jsonapi.services.yml
Drupal\jsonapi\Normalizer\HttpExceptionNormalizer

File

core/modules/jsonapi/src/Normalizer/HttpExceptionNormalizer.php, line 22

Namespace

Drupal\jsonapi\Normalizer
View source
class HttpExceptionNormalizer extends NormalizerBase {

  /**
   * The interface or class that this Normalizer supports.
   *
   * @var string
   */
  protected $supportedInterfaceOrClass = HttpException::class;

  /**
   * The current user making the request.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $currentUser;

  /**
   * HttpExceptionNormalizer constructor.
   *
   * @param \Drupal\Core\Session\AccountInterface $current_user
   *   The current user.
   */
  public function __construct(AccountInterface $current_user) {
    $this->currentUser = $current_user;
  }

  /**
   * {@inheritdoc}
   */
  public function normalize($object, $format = NULL, array $context = []) {
    $cacheability = new CacheableMetadata();
    $cacheability
      ->addCacheableDependency($object);
    return new HttpExceptionNormalizerValue($cacheability, static::rasterizeValueRecursive($this
      ->buildErrorObjects($object)));
  }

  /**
   * Builds the normalized JSON:API error objects for the response.
   *
   * @param \Symfony\Component\HttpKernel\Exception\HttpException $exception
   *   The Exception.
   *
   * @return array
   *   The error objects to include in the response.
   */
  protected function buildErrorObjects(HttpException $exception) {
    $error = [];
    $status_code = $exception
      ->getStatusCode();
    if (!empty(Response::$statusTexts[$status_code])) {
      $error['title'] = Response::$statusTexts[$status_code];
    }
    $error += [
      'status' => (string) $status_code,
      'detail' => $exception
        ->getMessage(),
    ];
    $error['links']['via']['href'] = \Drupal::request()
      ->getUri();

    // Provide an "info" link by default: if the exception carries a single
    // "Link" header, use that, otherwise fall back to the HTTP spec section
    // covering the exception's status code.
    $headers = $exception
      ->getHeaders();
    if (isset($headers['Link']) && !is_array($headers['Link'])) {
      $error['links']['info']['href'] = $headers['Link'];
    }
    elseif ($info_url = $this
      ->getInfoUrl($status_code)) {
      $error['links']['info']['href'] = $info_url;
    }

    // Exceptions thrown without an explicitly defined code get assigned zero by
    // default. Since this is no helpful information, omit it.
    if ($exception
      ->getCode() !== 0) {
      $error['code'] = (string) $exception
        ->getCode();
    }
    if ($this->currentUser
      ->hasPermission('access site reports')) {

      // The following information may contain sensitive information. Only show
      // it to authorized users.
      $error['source'] = [
        'file' => $exception
          ->getFile(),
        'line' => $exception
          ->getLine(),
      ];
      $error['meta'] = [
        'exception' => (string) $exception,
        'trace' => $exception
          ->getTrace(),
      ];
    }
    return [
      $error,
    ];
  }

  /**
   * Return a string to the common problem type.
   *
   * @return string|null
   *   URL pointing to the specific RFC-2616 section. Or NULL if it is an HTTP
   *   status code that is defined in another RFC.
   *
   * @see https://www.drupal.org/project/drupal/issues/2832211#comment-11826234
   *
   * @internal
   */
  public static function getInfoUrl($status_code) {

    // Depending on the error code we'll return a different URL.
    $url = 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html';
    $sections = [
      '100' => '#sec10.1.1',
      '101' => '#sec10.1.2',
      '200' => '#sec10.2.1',
      '201' => '#sec10.2.2',
      '202' => '#sec10.2.3',
      '203' => '#sec10.2.4',
      '204' => '#sec10.2.5',
      '205' => '#sec10.2.6',
      '206' => '#sec10.2.7',
      '300' => '#sec10.3.1',
      '301' => '#sec10.3.2',
      '302' => '#sec10.3.3',
      '303' => '#sec10.3.4',
      '304' => '#sec10.3.5',
      '305' => '#sec10.3.6',
      '307' => '#sec10.3.8',
      '400' => '#sec10.4.1',
      '401' => '#sec10.4.2',
      '402' => '#sec10.4.3',
      '403' => '#sec10.4.4',
      '404' => '#sec10.4.5',
      '405' => '#sec10.4.6',
      '406' => '#sec10.4.7',
      '407' => '#sec10.4.8',
      '408' => '#sec10.4.9',
      '409' => '#sec10.4.10',
      '410' => '#sec10.4.11',
      '411' => '#sec10.4.12',
      '412' => '#sec10.4.13',
      '413' => '#sec10.4.14',
      '414' => '#sec10.4.15',
      '415' => '#sec10.4.16',
      '416' => '#sec10.4.17',
      '417' => '#sec10.4.18',
      '500' => '#sec10.5.1',
      '501' => '#sec10.5.2',
      '502' => '#sec10.5.3',
      '503' => '#sec10.5.4',
      '504' => '#sec10.5.5',
      '505' => '#sec10.5.6',
    ];
    return empty($sections[$status_code]) ? NULL : $url . $sections[$status_code];
  }

}

Members

Namesort descending Modifiers Type Description Overrides
CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY constant Name of key for bubbling cacheability metadata via serialization context.
HttpExceptionNormalizer::$currentUser protected property The current user making the request.
HttpExceptionNormalizer::$supportedInterfaceOrClass protected property The interface or class that this Normalizer supports. Overrides NormalizerBase::$supportedInterfaceOrClass 2
HttpExceptionNormalizer::buildErrorObjects protected function Builds the normalized JSON:API error objects for the response. 2
HttpExceptionNormalizer::getInfoUrl public static function Return a string to the common problem type.
HttpExceptionNormalizer::normalize public function
HttpExceptionNormalizer::__construct public function HttpExceptionNormalizer constructor.
NormalizerBase::$format protected property List of formats which supports (de-)normalization. Overrides NormalizerBase::$format
NormalizerBase::addCacheableDependency protected function Adds cacheability if applicable.
NormalizerBase::checkFormat protected function Checks if the provided format is supported by this normalizer. Overrides NormalizerBase::checkFormat
NormalizerBase::rasterizeValueRecursive protected static function Rasterizes a value recursively.
NormalizerBase::supportsDenormalization public function Implements \Symfony\Component\Serializer\Normalizer\DenormalizerInterface::supportsDenormalization() 1
NormalizerBase::supportsNormalization public function 1