Commit 83c181f0 authored by catch's avatar catch
Browse files

Issue #3072076 by JordanDukart, logickal, gabesullice, cgoffin,...

Issue #3072076 by JordanDukart, logickal, gabesullice, cgoffin, patrickfweston, Wim Leers, alexpott, scuba_fly, larowlan, nehiryeli, xjm, borisson_: JSON:API returns a CacheableResponseInterface instance for non-cacheable methods; causes unnecessary exceptions
parent f0d27d2f
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\jsonapi;

use Drupal\Core\Cache\CacheableResponseInterface;
use Drupal\Core\Cache\CacheableResponseTrait;

/**
 * Extends ResourceResponse with cacheability.
 *
 * We want to have the same functionality for both responses that are cacheable
 * and those that are not.  This response class should be used in all instances
 * where the response is expected to be cacheable.
 *
 * @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.
 *
 * @see https://www.drupal.org/project/drupal/issues/3032787
 * @see jsonapi.api.php
 */
class CacheableResourceResponse extends ResourceResponse implements CacheableResponseInterface {

  use CacheableResponseTrait;

}
+13 −4
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\CacheableResponseInterface;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
@@ -26,6 +27,7 @@
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\jsonapi\Access\EntityAccessChecker;
use Drupal\jsonapi\CacheableResourceResponse;
use Drupal\jsonapi\Context\FieldResolver;
use Drupal\jsonapi\Entity\EntityValidationTrait;
use Drupal\jsonapi\Access\TemporaryQueryGuard;
@@ -279,7 +281,6 @@ public function createIndividual(ResourceType $resource_type, Request $request)
    // we should send "Location" header to the frontend.
    if ($resource_type->isLocatable()) {
      $url = $resource_object->toUrl()->setAbsolute()->toString(TRUE);
      $response->addCacheableDependency($url);
      $response->headers->set('Location', $url->getGeneratedUrl());
    }

@@ -536,7 +537,9 @@ function (EntityInterface $entity) {

    // $response does not contain the entity list cache tag. We add the
    // cacheable metadata for the finite list of entities in the relationship.
    if ($response instanceof CacheableResponseInterface) {
      $response->addCacheableDependency($entity);
    }

    return $response;
  }
@@ -567,7 +570,9 @@ public function getRelationship(ResourceType $resource_type, FieldableEntityInte
    $relationship = Relationship::createFromEntityReferenceField($resource_object, $field_list);
    $response = $this->buildWrappedResponse($relationship, $request, $this->getIncludes($request, $resource_object), $response_code);
    // Add the host entity as a cacheable dependency.
    if ($response instanceof CacheableResponseInterface) {
      $response->addCacheableDependency($entity);
    }
    return $response;
  }

@@ -994,7 +999,11 @@ protected function buildWrappedResponse(TopLevelDataInterface $data, Request $re
      $self_link = new Link(new CacheableMetadata(), self::getRequestLink($request), 'self');
      $links = $links->withLink('self', $self_link);
    }
    $response = new ResourceResponse(new JsonApiDocumentTopLevel($data, $includes, $links, $meta), $response_code, $headers);
    $document = new JsonApiDocumentTopLevel($data, $includes, $links, $meta);
    if (!$request->isMethodCacheable()) {
      return new ResourceResponse($document, $response_code, $headers);
    }
    $response = new CacheableResourceResponse($document, $response_code, $headers);
    $cacheability = (new CacheableMetadata())->addCacheContexts([
      // Make sure that different sparse fieldsets are cached differently.
      'url.query_args:fields',
+2 −2
Original line number Diff line number Diff line
@@ -6,12 +6,12 @@
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\jsonapi\CacheableResourceResponse;
use Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel;
use Drupal\jsonapi\JsonApiResource\LinkCollection;
use Drupal\jsonapi\JsonApiResource\NullIncludedData;
use Drupal\jsonapi\JsonApiResource\Link;
use Drupal\jsonapi\JsonApiResource\ResourceObjectData;
use Drupal\jsonapi\ResourceResponse;
use Drupal\jsonapi\ResourceType\ResourceType;
use Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -123,7 +123,7 @@ public function index() {
      }
    }

    $response = new ResourceResponse(new JsonApiDocumentTopLevel(new ResourceObjectData([]), new NullIncludedData(), $urls, $meta));
    $response = new CacheableResourceResponse(new JsonApiDocumentTopLevel(new ResourceObjectData([]), new NullIncludedData(), $urls, $meta));
    return $response->addCacheableDependency($cacheability);
  }

+9 −2
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@

namespace Drupal\jsonapi\EventSubscriber;

use Drupal\jsonapi\CacheableResourceResponse;
use Drupal\jsonapi\JsonApiResource\ErrorCollection;
use Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel;
use Drupal\jsonapi\JsonApiResource\LinkCollection;
@@ -58,8 +59,14 @@ public function onException(ExceptionEvent $event) {
  protected function setEventResponse(ExceptionEvent $event, $status) {
    /* @var \Symfony\Component\HttpKernel\Exception\HttpException $exception */
    $exception = $event->getThrowable();
    $response = new ResourceResponse(new JsonApiDocumentTopLevel(new ErrorCollection([$exception]), new NullIncludedData(), new LinkCollection([])), $exception->getStatusCode(), $exception->getHeaders());
    $document = new JsonApiDocumentTopLevel(new ErrorCollection([$exception]), new NullIncludedData(), new LinkCollection([]));
    if ($event->getRequest()->isMethodCacheable()) {
      $response = new CacheableResourceResponse($document, $exception->getStatusCode(), $exception->getHeaders());
      $response->addCacheableDependency($exception);
    }
    else {
      $response = new ResourceResponse($document, $exception->getStatusCode(), $exception->getHeaders());
    }
    $event->setResponse($response);
  }

+4 −2
Original line number Diff line number Diff line
@@ -120,8 +120,10 @@ protected function renderResponseBody(Request $request, ResourceResponse $respon
      $jsonapi_doc_object = $serializer->normalize($data, $format, $context);
      // Having just normalized the data, we can associate its cacheability with
      // the response object.
      if ($response instanceof CacheableResponseInterface) {
        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));
Loading