When creating a custom resource whenever I load something from storage (like a node) I get a failure at the endpoint:
Uncaught PHP Exception LogicException: "The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\Core\Cache\CacheableResponse." at /var/www/drupal-8.2.x-dev/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php line 159"

My custom ServiceDefinition is quite simple and all is well until it hits the EarlyRenderingControllerWrapperSubscriber. It seems that some toUrl function gets called touching the cache outside of context. I have this with 8.0.5, 8.1.0-beta2 and 8.2-dev of drupal.

My resource

namespace Drupal\MyModule\Plugin\ServiceDefinition;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Flood\FloodInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\services\ServiceDefinitionBase;
use Drupal\user\UserAuthInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\Routing\Route;
use Symfony\Component\Serializer\Encoder\DecoderInterface;
use Symfony\Component\Serializer\SerializerInterface;

/**
 * @ServiceDefinition(
 *   id = "node_extended",
 *   methods = {
 *     "GET"
 *   },
 *   title = @Translation("Extended Node Resource"),
 *   description = @Translation("."),
 *   translatable = false,
 *   category = @Translation("Node"),
 *   path = "node_extended"
 * )
 *
 */
class NodeExtended extends ServiceDefinitionBase implements ContainerFactoryPluginInterface {

  /**
   * Constructs a HTTP basic authentication provider object.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The config factory.
   * @param \Drupal\user\UserAuthInterface $user_auth
   *   The user authentication service.
   * @param \Drupal\Core\Flood\FloodInterface $flood
   *   The flood service.
   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
   *   The entity manager service.
   * @param Session $session
   */
  public function __construct($configuration, $plugin_id, $plugin_definition, ConfigFactoryInterface $config_factory, UserAuthInterface $user_auth, FloodInterface $flood, EntityManagerInterface $entity_manager, Session $session) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->configFactory = $config_factory;
    $this->userAuth = $user_auth;
    $this->flood = $flood;
    $this->entityManager = $entity_manager;
    $this->session = $session;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('config.factory'),
      $container->get('user.auth'),
      $container->get('flood'),
      $container->get('entity.manager'),
      $container->get('session')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function processRoute(Route $route) {
    $route->setRequirement('_user_is_logged_in', 'TRUE');
  }

  /**
   * {@inheritdoc}
   */
 <strong> public function processRequest(Request $request, RouteMatchInterface $route_match, SerializerInterface $serializer) {
    return $this->entityManager->getStorage('node')->load(1);
  }</strong>
}

Comments

sblommers created an issue. See original summary.

sblommers’s picture

Issue summary: View changes
kylebrowning’s picture

Category: Bug report » Support request
Gravypower’s picture

Maybe call toArray() on the entity once it has been loaded?

 /**
   * {@inheritdoc}
   */
  public function processRequest(Request $request, RouteMatchInterface $route_match, SerializerInterface $serializer) {
    return $this->entityManager->getStorage('node')->load(1)->toArray();
  }
}

Aaron

Gravypower’s picture

I ran into this issue again so did some more digging, I think what you need to do is out lined at https://www.drupal.org/node/2638686. I wrapped my call to the entityManager and renderer in a RenderContext.

$event = Node::load($nodeId);

$context = new RenderContext();

$response = $this->renderer->executeInRenderContext($context, function()  use ($event, $siteId) {
  $t = $this->entityManager->getViewBuilder($event->getEntityTypeId())->view($event, $siteId);
   return $this->renderer->render($t)->jsonSerialize();
});
Anybody’s picture

Version: 8.x-4.0-alpha3 » 5.x-dev
Status: Active » Postponed (maintainer needs more info)

@Gravypower could you perhaps describe how to reproduce this in a vanilla Drupal environment and prepare a MR to fix this in the services module?

That would be super helpful. Or was this a specific issue unrelated to services?