ResourceResponseSubscriber.php in JSON:API 8
Same filename and directory in other branches
Namespace
Drupal\jsonapi\EventSubscriberFile
src/EventSubscriber/ResourceResponseSubscriber.phpView source
<?php
namespace Drupal\jsonapi\EventSubscriber;
use Drupal\Core\Cache\CacheableResponse;
use Drupal\Core\Cache\CacheableResponseInterface;
use Drupal\jsonapi\Normalizer\Value\JsonApiDocumentTopLevelNormalizerValue;
use Drupal\jsonapi\ResourceResponse;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Serializer\SerializerInterface;
/**
* Response subscriber that serializes and removes ResourceResponses' data.
*
* @see \Drupal\rest\EventSubscriber\ResourceResponseSubscriber
* @internal
*
* This is 99% identical to:
*
* \Drupal\rest\EventSubscriber\ResourceResponseSubscriber
*
* but with a few differences:
* 1. It has the @jsonapi.serializer_do_not_use_removal_imminent service
* injected instead of @serializer
* 2. It has the @current_route_match service no longer injected
* 3. It hardcodes the format to 'api_json'
* 4. It adds the JsonApiDocumentTopLevelNormalizerValue value object returned
* by JSON API normalization to the response object.
* 5. It flattens only to a cacheable response if the HTTP method is cacheable.
*/
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.
$jsonapi_doc_object = $serializer
->normalize($data, $format, [
'request' => $request,
'resource_type' => $request
->get('resource_type'),
]);
// Having just normalized the data, we can associate its cacheability with
// the response object.
assert($jsonapi_doc_object instanceof JsonApiDocumentTopLevelNormalizerValue);
$response
->addCacheableDependency($jsonapi_doc_object);
// Finally, encode the normalized data (JSON API's encoder rasterizes it
// automatically).
$response
->setContent($serializer
->encode($jsonapi_doc_object, $format));
$response->headers
->set('Content-Type', $request
->getMimeType($format));
}
}
/**
* 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());
$final_response
->setCharset($response
->getCharset());
$final_response->headers = clone $response->headers;
if ($final_response instanceof CacheableResponseInterface) {
$final_response
->addCacheableDependency($response
->getCacheableMetadata());
}
return $final_response;
}
}
Classes
Name | Description |
---|---|
ResourceResponseSubscriber | Response subscriber that serializes and removes ResourceResponses' data. |