.../EntityTestLabelHalJsonAnonTest.php | 1 + .../EntityResource/Item/ItemHalJsonTestBase.php | 1 + .../EventSubscriber/ResourceResponseSubscriber.php | 35 +++++++++++++++++----- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/core/modules/hal/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelHalJsonAnonTest.php index 9c6ecdf..f95ea5c 100644 --- a/core/modules/hal/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelHalJsonAnonTest.php +++ b/core/modules/hal/tests/src/Functional/EntityResource/EntityTestLabel/EntityTestLabelHalJsonAnonTest.php @@ -95,6 +95,7 @@ protected function getNormalizedPostEntity() { */ protected function getExpectedCacheContexts() { return [ + 'url.site', 'user.permissions', ]; } diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Item/ItemHalJsonTestBase.php b/core/modules/hal/tests/src/Functional/EntityResource/Item/ItemHalJsonTestBase.php index fb2a8d2..24157e5 100644 --- a/core/modules/hal/tests/src/Functional/EntityResource/Item/ItemHalJsonTestBase.php +++ b/core/modules/hal/tests/src/Functional/EntityResource/Item/ItemHalJsonTestBase.php @@ -92,6 +92,7 @@ protected function getNormalizedPostEntity() { */ protected function getExpectedCacheContexts() { return [ + 'url.site', 'user.permissions', ]; } diff --git a/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php b/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php index c85ba57..fcade94 100644 --- a/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php +++ b/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php @@ -2,6 +2,7 @@ namespace Drupal\rest\EventSubscriber; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Cache\CacheableResponse; use Drupal\Core\Cache\CacheableResponseInterface; use Drupal\Core\Render\RenderContext; @@ -126,11 +127,18 @@ public function getResponseFormat(RouteMatchInterface $route_match, Request $req /** * 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. + * During serialization, encoders and normalizers are able to explicitly + * bubble cacheability metadata via the 'cacheability' key-value pair in the + * received context. This bubbled cacheability metadata will be applied to the + * the response. + * + * In prior versions of Drupal 8, we allowed implicit bubbling of cacheability + * metadata because there was no explicit cacheability metadata bubbling API. + * To maintain backwards compatibility, we continue to support this, but + * support for this will be dropped in Drupal 9.0.0. This is especially useful + * when interacting with APIs that implicitly invoke rendering (for example: + * generating URLs): this allows those to "leak", and we collect their bubbled + * cacheability metadata automatically in a render context. * * @param \Symfony\Component\HttpFoundation\Request $request * The request object. @@ -150,15 +158,26 @@ protected function renderResponseBody(Request $request, ResourceResponseInterfac // If there is data to send, serialize and set it as the response body. if ($data !== NULL) { + $serialization_context = [ + 'request' => $request, + 'cacheability' => new CacheableMetadata(), + ]; + + // @deprecated In Drupal 8.4.0, will be removed before Drupal 9.0.0. Use + // explicit cacheability metadata bubbling instead. (The wrapping call to + // executeInRenderContext() will be removed before Drupal 9.0.0.) $context = new RenderContext(); - $serialization_contexts = ['request' => $request]; + $output = $this->renderer + ->executeInRenderContext($context, function() use ($serializer, $data, $format, $serialization_context) { + return $serializer->serialize($data, $format, $serialization_context); + }); if ($response instanceof CacheableResponseInterface) { if (!$context->isEmpty()) { + @trigger_error('Implicit cacheability metadata bubbling (onto the global render context) in normalizers is deprecated since Drupal 8.4.0 and will be removed in Drupal 9.0.0. Use the "cacheability" serialization context instead, for explicit cacheability metadata bubbling.', E_USER_DEPRECATED); $response->addCacheableDependency($context->pop()); } - $serialization_contexts['cacheability'] = $response->getCacheableMetadata(); + $response->addCacheableDependency($serialization_context['cacheability']); } - $output = $serializer->serialize($data, $format, $serialization_contexts); $response->setContent($output); $response->headers->set('Content-Type', $request->getMimeType($format));