web-dev-qa-db-fra.com

Impossible de trouver un cookie pour validation dans EventSubscriber

Il y a ce module Splash Redirect dans lequel src/EventSubscriber/SplashRedirectEventSubscriber.php est censé vérifier si le cookie $config_cookie existe ou non, s'il existe déjà, l'utilisateur continue sur le nœud demandé ($config_source), mais s'il n'existe pas , l'utilisateur est envoyé à $config_destination et il crée le cookie ($config_cookie).

Il réussit à créer le $config_cookie et la redirection vers $config_destination mais il ne semble jamais reconnaître l'existence du cookie une fois créé et continue de rediriger l'utilisateur vers $config_destination.

Des indices? On Drupal 8.6

<?php

namespace Drupal\splash_redirect\EventSubscriber;

use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Drupal\Core\Routing\TrustedRedirectResponse;

/**
 * Splash redirect Event Subscriber.
 */
class SplashRedirectEventSubscriber implements EventSubscriberInterface {

  /**
   * Triggered when system sends response.
   */
  public function modifyIntercept(GetResponseEvent $event) {
    $config = \Drupal::config('splash_redirect.settings');
    $config_enabled = $config->get('splash_redirect.is_enabled');
    $config_source = $config->get('splash_redirect.source');
    $config_destination = $config->get('splash_redirect.destination');
    $config_cookie = $config->get('splash_redirect.cookie_name');
    $config_duration = $config->get('splash_redirect.duration');

    // If splash config is not enabled then we don't need to do any of this.
    if ($config_enabled == 1) {
      // Current request from client.
      $request = \Drupal::request();
      $current_uri = $request->getRequestUri();
      $http_Host = $request->getHost();
      // Current response from system.
      $response = $event->getResponse();
      $route = (\Drupal::routeMatch()->getParameter('node')) ? \Drupal::routeMatch()->getParameter('node')->id() : null;
      $has_cookie = $request->cookies->has($config_cookie);

      // If splash-cookie has not been set, and user requesting 'source' page,
      // set cookie and redirect to splash page.
      if ($config_source == $route) {
        // Kill cache on this route or else cookie might not be read with VCL.
        \Drupal::service('page_cache_kill_switch')->trigger();

        if (!$has_cookie) {
          // Set redirect response with cookie and redirect location.
          $redir = new TrustedRedirectResponse($config_destination, '302');
          $cookie = new Cookie($config_cookie, 'true', strtotime('now + ' . $config_duration . 'days'), '/', '.' . $http_Host, false, true);
          $redir->headers->setCookie($cookie);
          $redir->headers->set('Cache-Control', 'public, max-age=0');
          $redir->addCacheableDependency($config_destination);
          $event->setResponse($redir);
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    // Listen for response event from system and intercept.
    $events[KernelEvents::REQUEST][] = ['modifyIntercept'];
    return $events;
  }

}
5
Kris Robinson

Quelques idées:

Comme pour la configuration, vous devez également ajouter une dépendance de cache pour le cookie:

  $response->getCacheableMetadata()->addCacheContexts(['cookies:' . $config_cookie]);

Ou utilisez un RedirectResponse non mis en cache pour simplifier les choses.

Et essayez de définir une priorité pour l'abonné à l'événement, par exemple 31, car votre code dépend uniquement du routage, qui en a 32:

  $events[KernelEvents::REQUEST][] = ['modifyIntercept', 31];

Vous n'avez pas besoin d'obtenir une réponse dans un abonné de demande, car il n'y en a pas encore. Obtenez la demande à la place et assurez-vous que vous êtes dans une demande principale. Voici donc comment l'abonné démarre la plupart du temps:

  public function onKernelRequest(GetResponseEvent $event) {
    if (!$event->isMasterRequest()) {
      return;
    }
    $request = $event->getRequest();
    ...

Simplifier un peu plus et utiliser des valeurs fictives pour les tests:

  public function onKernelRequest(GetResponseEvent $event) {
    if (!$event->isMasterRequest()) {
      return;
    }
    $route_match = \Drupal::routeMatch();
    if (!$route_match->getRouteName() == 'entity.node.canonical') {
      return;
    }
    $request = $event->getRequest();
    if ($route_match->getRawParameter('node') == '1') {
      \Drupal::service('page_cache_kill_switch')->trigger();
      if (!$request->cookies->has('foo')) {
        $redirect = new RedirectResponse($request->getBasePath() . '/node/2', 302);
        $redirect->headers->setCookie(new Cookie('foo', '123', '+30 seconds'));
        $event->setResponse($redirect);
      }
    }
  }

Cela fonctionne sur mon environnement de développement local.


Code complet (y compris l'injection de dépendance):

/ src/EventSubscriber/MymoduleSubscriber.php

<?php

namespace Drupal\mymodule\EventSubscriber;

use Drupal\Core\Routing\RouteMatchInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Cookie;

/**
 * mymodule event subscriber.
 */
class MymoduleSubscriber implements EventSubscriberInterface {

  /**
   * The route match.
   *
   * @var \Drupal\Core\Routing\RouteMatchInterface
   */
  protected $routeMatch;

  /**
   * Constructs event subscriber.
   *
   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
   *   The route match.
   */
  public function __construct(RouteMatchInterface $route_match) {
    $this->routeMatch = $route_match;
  }

  /**
   * Kernel request event handler.
   *
   * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
   *   Response event.
   */
  public function onKernelRequest(GetResponseEvent $event) {
    if (!$event->isMasterRequest()) {
      return;
    }
    if (!$this->routeMatch->getRouteName() == 'entity.node.canonical') {
      return;
    }
    $request = $event->getRequest();
    if ($this->routeMatch->getRawParameter('node') == '1') {
      \Drupal::service('page_cache_kill_switch')->trigger();
      if (!$request->cookies->has('foo')) {
        $redirect = new RedirectResponse($request->getBasePath() .'/node/2', 302);
        $redirect->headers->setCookie(new Cookie('foo', '123', '+30 seconds'));
        $event->setResponse($redirect);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    return [
      KernelEvents::REQUEST => ['onKernelRequest', 31],
    ];
  }

}

mymodule.services.yml

services:
  mymodule.event_subscriber:
    class: Drupal\mymodule\EventSubscriber\MymoduleSubscriber
    arguments: ['@current_route_match']
    tags:
      - { name: event_subscriber }
6
4k4

Original splash_redirect module dev ici. Merci de m'avoir rejoint dans le trou du lapin en cache! (Désolé, je ne peux pas encore faire de "commentaires", je dois donc mettre cela dans une réponse)

Merci à @ 4k4 pour les excellentes suggestions, j'en implémenterai probablement certaines dans la prochaine version, mais le problème d'OP semble être spécifique au CDN mondial de Panthéon, qui définit le Vary: cookie header et met en cache la redirection source.

* Donc, si vous utilisez splash_redirect sur Pantheon, veuillez préfixer votre nom de cookie Splash avec "SESS" , par exemple SESSsplash *

4
AdamB