Change record status: 
Project: 
Introduced in branch: 
8.x
Description: 

As of Drupal 8 drupal_goto() has been removed in favor of RedirectResponse which comes together with the Symfony HttpFoundation.

hook_drupal_goto_alter() was also removed as well, in favor of just using an event listener on kernel.response

Redirecting

Drupal 7

function my_menu_callback_foo() {
  //....
  drupal_goto('user');
}

Drupal 8 (procedural)


use Symfony\Component\HttpFoundation\RedirectResponse;

function my_menu_callback_foo() {
  //....
  return new RedirectResponse(\Drupal\Core\Url::fromRoute('user.page')->toString());
}

Drupal 8 (properly injected)


use Drupal\Core\Controller\ControllerBase;

class MyControllerClass extends ControllerBase {

  public function foo() {
    //...
    return $this->redirect('user.page');
  }
}

If the route name is not known use RedirectResponse with a URL. When redirecting to an external location, use TrustedRedirectResponse. (Note that if Drupal has a destination set, it will unexpectedly override all redirects).


use Drupal\Core\Controller\ControllerBase;

class MyControllerClass extends ControllerBase {

  public function foo() {
    //...
    return new TrustedRedirectResponse('https://example.com/some/path'); // for eg. Url::fromUserInput(\Drupal::destination()->get())->setAbsolute()->toString();
  }
}

Note: #2023445: Improve DX of doing a redirect might change the way this is handled.

Altering the redirection URL:

D7

/**
 * Implements hook_drupal_goto_alter().
 */
function mymodule_drupal_goto_alter(&$path, &$options, &$http_response_code) {
  if  ($path == 'http://example.com') {
     $path = 'http://example2.com';
  }
}

D8
in mymodule.services.yml:

services:
  mymodule.subscriber:
    class: Drupal\mymodule\EventSubscriber\MyModuleSubscriber
    tags:
      - { name: event_subscriber }
namespace Drupal\mymodule\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class MyModuleSubscriber implements EventSubscriberInterface {

  public function checkRedirection(FilterResponseEvent $event) {
    $response = $event->getResponse();
    if ($response instanceOf RedirectResponse && $response->getTargetUrl() == 'http://example.com') {
       $response->setTargetUrl('http://example2.com');
    } 
  }

  static function getSubscribedEvents() {
    $events[KernelEvents::RESPONSE][] = ['checkRedirection'];
    return $events;
  }
}

Redirecting to the same page

You should avoid doing this dynamic lookup and specify the route you wish to redirect to.
Note that all usages of this in core were replaced by specific redirects.
Drupal 7

function my_menu_callback_foo() {
  //....
  drupal_goto();
}

Drupal 8


use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Routing\RouteMatch;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;

class MyControllerClass extends ControllerBase {

  public function foo(Request $request) {
    $route_match = $route = RouteMatch::createFromRequest($request);
    //...
    return $this->redirect($route_match->getRouteName(), $route_match->getRawParameters()->all());
  }
}

Redirecting to the home page

Drupal 7

function my_menu_callback_foo() {
  //....
  drupal_goto('');
}

Drupal 8


use Drupal\Core\Controller\ControllerBase;

class MyControllerClass extends ControllerBase {

  public function foo(Request $request) {
    //...
    return $this->redirect('<front>');
  }
}

Redirecting when not in context of a controller

  $response = new RedirectResponse($url->toString());
  $request = \Drupal::request();
  // Save the session so things like messages get saved.
  $request->getSession()->save();
  $response->prepare($request);
  // Make sure to trigger kernel events.
  \Drupal::service('kernel')->terminate($request, $response);
  $response->send();
Impacts: 
Module developers

Comments

DWB Internet’s picture

The RedirectResponse object doesn't allow for external redirects anymore. For external redirects use \Drupal\Core\Routing\TrustedRedirectResponse:

<?php
use Drupal\Core\Routing\TrustedRedirectResponse;

public function myControllerMethod() {
  return new TrustedRedirectResponse('http://example.com/foo/bar/');
}
kalidasan’s picture

use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\Core\Url;

$redirect = new RedirectResponse(Url::fromUserInput('/your/page/path')->toString());;
$redirect->send();
Above code used in form submit
mikedotexe’s picture

Our business logic demanded a redirect in a preprocess. This page had an anchor/fragment.
So redirecting to https://example.com/node-19-alias#conclusion
Can be achieved by:

$response = new RedirectResponse(\Drupal\Core\Url::fromRoute('entity.node.canonical', ['node' => 19], ['fragment' => 'conclusion'])->toString());
$response->send();

Hakuna Matata

mxr576’s picture

Is "Redirecting when not in context of a controller" still working or is it triggers an error like this for others too?

RuntimeException: Failed to start the session because headers have already been sent by "vendor/symfony/http-foundation/Response.php" at line 368. in Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage->start() (line 152 of vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php) 

See: https://www.drupal.org/project/webform/issues/3380667

birk’s picture

I recently had some caching issues with the "Redirecting when not in context of a controller". And without knowing about the existing EnforcedResponseException solution, I implemented an almost identical workaround.
I'll make a shameless plug to an article where I summarize the implementation: https://blog.birk-jensen.dk/drupal-http-redirection-from-anywhere

interjinn’s picture

Unfortunately the following redirect method doesn't trigger the KernelEvents::RESPONSE event:

  $response = new RedirectResponse($url->toString());
  $request = \Drupal::request();

  // Save the session so things like messages get saved.
  $request->getSession()->save();
  $response->prepare($request);

  // Make sure to trigger kernel events.
  \Drupal::service('kernel')->terminate($request, $response);
  $response->send();

The trigger lies in the Kernel and requires the dispatcher which is a protected member of the Kernel.