1

I have a content type that needs to redirect only when it is tagged as "External". All of these "External" items have a field where a URL is supplied for the redirect.

During initial setup of the content type the redirection was being done in the .theme file in preprocess_html but we discovered that after the first time someone was redirected when accessing one of these nodes, it would get cached and the redirection would not longer happen.

I then built a module to do the redirection with and event subscriber and this fixed the problem when logged in as an admin but not for anonymous users.

For the third iteration, I built a module to set up middleware that implements HttpKernelInterface in order to do the redirection there. This particular content type had a url pattern so I used that to determine when the middleware should run based on the $request->getPathInfo(); and then testing for the node type and the "External" tag being applied to it. That solved all of the redirection problems, but other started to pop up. For example, the edit form for all of the nodes started displaying on the the title field and nothing else and views using the content type would start displaying as empty as well.

Is there a more effective way of doing this redirection and avoiding the page cache than doing it with middleware or has anyone experienced an issue like this when implementing HttpKernelInterface?

handle function being used in the middleware:

public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {


$newsPrefix = "/news";

$current_uri = $request->getPathInfo();
if(substr($current_uri, 0, strlen($newsPrefix)) === $newsPrefix){
  $path = \Drupal::service('path.alias_manager')->getPathByAlias($current_uri);

  if(preg_match('/node\/(\d+)/', $path, $matches)) {
    $node = \Drupal\node\Entity\Node::load($matches[1]);

    if (isset($node)) {

      $nodeType = $node->getType();
      if($nodeType === 'news'){

        if(isset($node->news_type_of_content) && isset($node->news_type_of_content->target_id)){
          $typeID = $node->news_type_of_content->target_id;

          if(isset($typeID)){
            $term = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->load($typeID);
            $name = $term->getName();
          }

          if(isset($name) && $name === "External" && isset($node->news_source_url->value)){

            $response = new RedirectResponse($node->news_source_url->value, 302);
            $response->send();
            exit;
          }
        }
      }
    }
  }
}
return $this->httpKernel->handle($request, $type, $catch);

}

4
  • 5
    It's hard to identify the correct route so early in a middleware and not to catch more requests than you want, which causes the side effects you've described. This should have worked in your second iteration (in an event subscriber) as long as the redirect depends only on static database content and you use a cacheable response with the correct cache metadata. See drupal.stackexchange.com/questions/278698/…
    – 4uk4
    Commented Nov 6, 2019 at 20:38
  • Exactly what I needed. Thanks for the suggestion! Commented Nov 7, 2019 at 20:02
  • Are you OK with marking this as duplicate? Your question actually fits better to the answer over there than the original one, which is unclear about how dynamic the condition is.
    – 4uk4
    Commented Nov 7, 2019 at 21:14
  • Or do you want to post the final solution?
    – 4uk4
    Commented Nov 7, 2019 at 21:21

1 Answer 1

2

Solution ended up being an event subscriber with a cacheable response:


<?php


namespace Drupal\news_redirect\EventSubscriber;

use Drupal\Core\Routing\TrustedRedirectResponse;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;


class NewsRedirectSubscriber implements EventSubscriberInterface {

  public function redirectMediaCoverage(GetResponseEvent $event) {

    $node = \Drupal::routeMatch()->getParameter('node');
    if (isset($node) && $node instanceof \Drupal\node\NodeInterface) {

      $nodeType = $node->getType();

      if($nodeType === 'news'){

        $typeID = $node->news_type_of_content->target_id;

        if(isset($typeID)){

          $term = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->load($typeID);
          $name = $term->getName();

        }

        if(isset($name) && $name === "Media Coverage" && isset($node->news_source_url->value)){

          $url = $node->news_source_url->value;

          if(isset($url)){

            $response_headers = [
              'Cache-Control' => 'no-cache, no-store, must-revalidate',
            ];

            $response = new TrustedRedirectResponse($url, '302', $response_headers);
            $response->addCacheableDependency($node);
            $event->setResponse($response);
          }

        }

      }

    }

  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events[KernelEvents::REQUEST][] = ['redirectMediaCoverage',30];
    return $events;
  }

}


2
  • 1
    Great answer, you might also want to check the route name, so that you don't redirect for example on node edit routes, see drupal.stackexchange.com/questions/193025/…
    – 4uk4
    Commented Nov 8, 2019 at 7:22
  • is the addCacheableDependency() call necessary given the values in your Cache-Control response headers? Commented May 9, 2023 at 15:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.