class ResourceResponseSubscriber

Same name in this branch
  1. 8.9.x core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php \Drupal\rest\EventSubscriber\ResourceResponseSubscriber
Same name in other branches
  1. 9 core/modules/jsonapi/src/EventSubscriber/ResourceResponseSubscriber.php \Drupal\jsonapi\EventSubscriber\ResourceResponseSubscriber
  2. 9 core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php \Drupal\rest\EventSubscriber\ResourceResponseSubscriber
  3. 10 core/modules/jsonapi/src/EventSubscriber/ResourceResponseSubscriber.php \Drupal\jsonapi\EventSubscriber\ResourceResponseSubscriber
  4. 10 core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php \Drupal\rest\EventSubscriber\ResourceResponseSubscriber
  5. 11.x core/modules/jsonapi/src/EventSubscriber/ResourceResponseSubscriber.php \Drupal\jsonapi\EventSubscriber\ResourceResponseSubscriber
  6. 11.x core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php \Drupal\rest\EventSubscriber\ResourceResponseSubscriber

Response subscriber that serializes and removes ResourceResponses' data.

@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\ResourceResponseSubscriber implements \Symfony\Component\EventDispatcher\EventSubscriberInterface

Expanded class hierarchy of ResourceResponseSubscriber

See also

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

jsonapi.api.php

This is 99% identical to:

\Drupal\rest\EventSubscriber\ResourceResponseSubscriber

but with a few differences: 1. It has the @jsonapi.serializer service injected instead of @serializer 2. It has the @current_route_match service no longer injected 3. It hardcodes the Formatting to 'api_json' 4. It adds the CacheableNormalization object returned by JSON:API normalization to the response object. 5. It flattens only to a cacheable response if the HTTP method is cacheable.

\Drupal\rest\EventSubscriber\ResourceResponseSubscriber

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

File

core/modules/jsonapi/src/EventSubscriber/ResourceResponseSubscriber.php, line 39

Namespace

Drupal\jsonapi\EventSubscriber
View source
class ResourceResponseSubscriber implements EventSubscriberInterface {
    
    /**
     * The serializer.
     *
     * @var \Symfony\Component\Serializer\SerializerInterface
     */
    protected $serializer;
    
    /**
     * Constructs a ResourceResponseSubscriber object.
     *
     * @param \Symfony\Component\Serializer\SerializerInterface $serializer
     *   The serializer.
     */
    public function __construct(SerializerInterface $serializer) {
        $this->serializer = $serializer;
    }
    
    /**
     * {@inheritdoc}
     *
     * @see \Drupal\rest\EventSubscriber\ResourceResponseSubscriber::getSubscribedEvents()
     * @see \Drupal\dynamic_page_cache\EventSubscriber\DynamicPageCacheSubscriber
     */
    public static function getSubscribedEvents() {
        // Run before the dynamic page cache subscriber (priority 100), so that
        // Dynamic Page Cache can cache flattened responses.
        $events[KernelEvents::RESPONSE][] = [
            'onResponse',
            128,
        ];
        return $events;
    }
    
    /**
     * Serializes ResourceResponse responses' data, and removes that data.
     *
     * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
     *   The event to process.
     */
    public function onResponse(FilterResponseEvent $event) {
        $response = $event->getResponse();
        if (!$response instanceof ResourceResponse) {
            return;
        }
        $request = $event->getRequest();
        $format = 'api_json';
        $this->renderResponseBody($request, $response, $this->serializer, $format);
        $event->setResponse($this->flattenResponse($response, $request));
    }
    
    /**
     * Renders a resource response body.
     *
     * Serialization can invoke rendering (e.g., generating URLs), but the
     * serialization API does not provide a mechanism to collect the
     * bubbleable metadata associated with that (e.g., language and other
     * contexts), so instead, allow those to "leak" and collect them here in
     * a render context.
     *
     * @param \Symfony\Component\HttpFoundation\Request $request
     *   The request object.
     * @param \Drupal\jsonapi\ResourceResponse $response
     *   The response from the JSON:API resource.
     * @param \Symfony\Component\Serializer\SerializerInterface $serializer
     *   The serializer to use.
     * @param string|null $format
     *   The response format, or NULL in case the response does not need a format,
     *   for example for the response to a DELETE request.
     *
     * @todo Add test coverage for language negotiation contexts in
     *   https://www.drupal.org/node/2135829.
     */
    protected function renderResponseBody(Request $request, ResourceResponse $response, SerializerInterface $serializer, $format) {
        $data = $response->getResponseData();
        // If there is data to send, serialize and set it as the response body.
        if ($data !== NULL) {
            // First normalize the data. Note that error responses do not need a
            // normalization context, since there are no entities to normalize.
            // @see \Drupal\jsonapi\EventSubscriber\DefaultExceptionSubscriber::isJsonApiExceptionEvent()
            $context = !$response->isSuccessful() ? [] : static::generateContext($request);
            $jsonapi_doc_object = $serializer->normalize($data, $format, $context);
            // Having just normalized the data, we can associate its cacheability with
            // the response object.
            assert($jsonapi_doc_object instanceof CacheableNormalization);
            $response->addCacheableDependency($jsonapi_doc_object);
            // Finally, encode the normalized data (JSON:API's encoder rasterizes it
            // automatically).
            $response->setContent($serializer->encode($jsonapi_doc_object->getNormalization(), $format));
            $response->headers
                ->set('Content-Type', $request->getMimeType($format));
        }
    }
    
    /**
     * Generates a top-level JSON:API normalization context.
     *
     * @param \Symfony\Component\HttpFoundation\Request $request
     *   The request from which the context can be derived.
     *
     * @return array
     *   The generated context.
     */
    protected static function generateContext(Request $request) {
        // Build the expanded context.
        $context = [
            'account' => NULL,
            'sparse_fieldset' => NULL,
        ];
        if ($request->query
            ->get('fields')) {
            $context['sparse_fieldset'] = array_map(function ($item) {
                return explode(',', $item);
            }, $request->query
                ->get('fields'));
        }
        return $context;
    }
    
    /**
     * Flattens a fully rendered resource response.
     *
     * Ensures that complex data structures in ResourceResponse::getResponseData()
     * are not serialized. Not doing this means that caching this response object
     * requires deserializing the PHP data when reading this response object from
     * cache, which can be very costly, and is unnecessary.
     *
     * @param \Drupal\jsonapi\ResourceResponse $response
     *   A fully rendered resource response.
     * @param \Symfony\Component\HttpFoundation\Request $request
     *   The request for which this response is generated.
     *
     * @return \Drupal\Core\Cache\CacheableResponse|\Symfony\Component\HttpFoundation\Response
     *   The flattened response.
     */
    protected static function flattenResponse(ResourceResponse $response, Request $request) {
        $final_response = $response instanceof CacheableResponseInterface && $request->isMethodCacheable() ? new CacheableResponse() : new Response();
        $final_response->setContent($response->getContent());
        $final_response->setStatusCode($response->getStatusCode());
        $final_response->setProtocolVersion($response->getProtocolVersion());
        if ($charset = $response->getCharset()) {
            $final_response->setCharset($charset);
        }
        $final_response->headers = clone $response->headers;
        if ($final_response instanceof CacheableResponseInterface) {
            $final_response->addCacheableDependency($response->getCacheableMetadata());
        }
        return $final_response;
    }

}

Members

Title Sort descending Modifiers Object type Summary
ResourceResponseSubscriber::$serializer protected property The serializer.
ResourceResponseSubscriber::flattenResponse protected static function Flattens a fully rendered resource response.
ResourceResponseSubscriber::generateContext protected static function Generates a top-level JSON:API normalization context.
ResourceResponseSubscriber::getSubscribedEvents public static function
ResourceResponseSubscriber::onResponse public function Serializes ResourceResponse responses' data, and removes that data.
ResourceResponseSubscriber::renderResponseBody protected function Renders a resource response body.
ResourceResponseSubscriber::__construct public function Constructs a ResourceResponseSubscriber object.

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