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
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other: 
Other updates done

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