diff --git a/core/core.services.yml b/core/core.services.yml index afccb31..0722878 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -782,6 +782,11 @@ services: arguments: ['@route_filter.lazy_collector'] tags: - { name: event_subscriber } + route_normalizer_request_subscriber: + class: Drupal\Core\EventSubscriber\RouteNormalizerRequestSubscriber + arguments: ['@url_generator', '@path.matcher'] + tags: + - { name: event_subscriber } url_generator.non_bubbling: class: Drupal\Core\Routing\UrlGenerator arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@request_stack', '%filter_protocols%'] diff --git a/core/lib/Drupal/Core/EventSubscriber/RouteNormalizerRequestSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/RouteNormalizerRequestSubscriber.php new file mode 100644 index 0000000..163e8d5 --- /dev/null +++ b/core/lib/Drupal/Core/EventSubscriber/RouteNormalizerRequestSubscriber.php @@ -0,0 +1,106 @@ +urlGenerator = $url_generator; + $this->pathMatcher = $path_matcher; + } + + /** + * Performs a redirect if the URL changes in routing. + * + * The redirect happens if a URL constructed from the current route is + * different from the requested one. Examples: + * - Language negotiation system detected a language to use, and that language + * has a path prefix: perform a redirect to the language prefixed URL. + * - A route that set as the front page is requested: redirect to the front + * page. + * - Requested path has an alias: redirect to alias. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event + * The Event to process. + */ + public function onKernelRequestRedirect(GetResponseEvent $event) { + $request = $event->getRequest(); + $can_redirect = $event->isMasterRequest() + && $request->isMethod('GET') + && !$request->query->has('destination') + && RequestHelper::isCleanUrl($request) + && !$request->attributes->get('disable_route_normalizer'); + if ($can_redirect) { + // The "" placeholder can be used for all routes except the front + // page because it's not a real route. + $route_name = $this->pathMatcher->isFrontPage() ? '' : ''; + $options = [ + 'query' => $request->query->all(), + 'absolute' => TRUE, + ]; + $redirect_uri = $this->urlGenerator->generateFromRoute($route_name, [], $options); + $original_uri = $request->getSchemeAndHttpHost() . $request->getRequestUri(); + if ($redirect_uri != $original_uri) { + $response = new RedirectResponse($redirect_uri); + $response->headers->set('X-Drupal-Route-Normalizer', 1); + $event->setResponse($response); + } + } + } + + /** + * {@inheritdoc} + */ + static function getSubscribedEvents() { + // Execute after routes are initialized in + // \Drupal\Core\Routing\RoutePreloader::onRequest(). + $events[KernelEvents::REQUEST][] = array('onKernelRequestRedirect', -1); + return $events; + } + +} diff --git a/core/lib/Drupal/Core/Routing/UrlMatcher.php b/core/lib/Drupal/Core/Routing/UrlMatcher.php index bd1d6cb..061e839 100644 --- a/core/lib/Drupal/Core/Routing/UrlMatcher.php +++ b/core/lib/Drupal/Core/Routing/UrlMatcher.php @@ -44,8 +44,8 @@ public function finalMatch(RouteCollection $collection, Request $request) { $this->setContext($context); // The matcher expects raw path while we have it already decoded by the - // \Drupal\Core\PathProcessor\PathProcessorDecode. Encode it back so that - // route parameter are not double decoded. + // \Drupal\Core\PathProcessor\PathProcessorDecode. Encode it to avoid double + // decoding of the route parameters. $encoded_path = rawurlencode($this->currentPath->getPath($request)); return $this->match($encoded_path); diff --git a/core/modules/image/src/PathProcessor/PathProcessorImageStyles.php b/core/modules/image/src/PathProcessor/PathProcessorImageStyles.php index 15206a1..48bf809 100644 --- a/core/modules/image/src/PathProcessor/PathProcessorImageStyles.php +++ b/core/modules/image/src/PathProcessor/PathProcessorImageStyles.php @@ -69,6 +69,8 @@ public function processInbound($path, Request $request) { // Set the file as query parameter. $request->query->set('file', $file); + + // Disable route normalizer since we changed the request object. $request->attributes->set('disable_route_normalizer', TRUE); return $path_prefix . $image_style . '/' . $scheme; diff --git a/core/modules/language/src/Tests/LanguageListTest.php b/core/modules/language/src/Tests/LanguageListTest.php index 245fcbd..2f3f55d 100644 --- a/core/modules/language/src/Tests/LanguageListTest.php +++ b/core/modules/language/src/Tests/LanguageListTest.php @@ -154,11 +154,10 @@ function testLanguageList() { 'direction' => Language::DIRECTION_LTR, ); $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language')); - //As we changed the language configuration, rebuilt the container so that - // \Drupal\Core\Routing\UrlGeneratorInterface::generateFromRoute() can - // construct correct URL. + // As we changed the amount of languages, rebuilt the container so that + // \Drupal\language\LanguageServiceProvider (un)register its services and we + // can construct the correct URL. $this->rebuildContainer(); - //\Drupal::service('router.builder')->rebuild(); $this->assertUrl(\Drupal::url('entity.configurable_language.collection', [], ['absolute' => TRUE])); $this->assertText($name, 'Name found.'); diff --git a/core/modules/path/src/Tests/PathAliasTest.php b/core/modules/path/src/Tests/PathAliasTest.php index 4676684..5c50d21 100644 --- a/core/modules/path/src/Tests/PathAliasTest.php +++ b/core/modules/path/src/Tests/PathAliasTest.php @@ -42,8 +42,9 @@ function testPathCache() { // Since we have the route normalizer, all GET requests having path aliases // are redirected directly to that aliases, and no "preload-paths:" cache is // set. - // TODO Decide whether we want to remove/update the caching functionality of - // the \Drupal\Core\Path\AliasManager. + // @todo Decide whether the caching functionality of + // \Drupal\Core\Path\AliasManager should be removed/updated and whether this + // test should be removed/updated. return; // Create test node. diff --git a/core/modules/system/src/EventSubscriber/RouteNormalizerRequestSubscriber.php b/core/modules/system/src/EventSubscriber/RouteNormalizerRequestSubscriber.php deleted file mode 100644 index fe79255..0000000 --- a/core/modules/system/src/EventSubscriber/RouteNormalizerRequestSubscriber.php +++ /dev/null @@ -1,98 +0,0 @@ -urlGenerator = $url_generator; - $this->pathMatcher = $path_matcher; - } - - /** - * Performs a redirect if the path changed in routing. - * - * For example, when language negotiation selected a different language for - * the page. - * - * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event - * The Event to process. - */ - public function onKernelRequestRedirect(GetResponseEvent $event) { - $request = $event->getRequest(); - $can_redirect = $event->isMasterRequest() - && $request->isMethod('GET') - && !$request->query->has('destination') - && RequestHelper::isCleanUrl($request) - && !$request->attributes->get('disable_route_normalizer'); - if ($can_redirect) { - // Construct URL to the current route. If it is different from the request - // URL, then we assume that it was changed on a purpose (for example, to - // match the detected language) and perform a redirect. - $route_name = $this->pathMatcher->isFrontPage() ? '' : ''; - $options = [ - 'query' => $request->query->all(), - 'absolute' => TRUE, - ]; - $redirect_uri = $this->urlGenerator->generateFromRoute($route_name, [], $options); - $original_uri = $request->getSchemeAndHttpHost() . $request->getRequestUri(); - if ($redirect_uri != $original_uri) { - $response = new RedirectResponse($redirect_uri); - $response->headers->set('X-Drupal-Route-Normalizer', 1); - $event->setResponse($response); - } - } - } - - /** - * Registers the methods in this class that should be listeners. - * - * @return array - * An array of event listener definitions. - */ - static function getSubscribedEvents() { - // Execute after routes are initialized in - // \Drupal\Core\Routing\RoutePreloader::onRequest(). - $events[KernelEvents::REQUEST][] = array('onKernelRequestRedirect', -1); - return $events; - } - -} diff --git a/core/modules/system/src/Tests/Routing/ContentNegotiationRoutingTest.php b/core/modules/system/src/Tests/Routing/ContentNegotiationRoutingTest.php index 416b11c..571f6ef 100644 --- a/core/modules/system/src/Tests/Routing/ContentNegotiationRoutingTest.php +++ b/core/modules/system/src/Tests/Routing/ContentNegotiationRoutingTest.php @@ -115,7 +115,7 @@ function testContentRouting() { // see the error. $this->assertTrue(TRUE, $message); - // Handle redirect. + // Handle possible redirect. if ($response->isRedirect()) { $parsed = parse_url($response->headers->get('Location')); $path = $parsed['path']; diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml index 570fa3cd..c70889d 100644 --- a/core/modules/system/system.services.yml +++ b/core/modules/system/system.services.yml @@ -23,11 +23,6 @@ services: class: Drupal\system\EventSubscriber\AdminRouteSubscriber tags: - { name: event_subscriber } - system.route_normalizer_request_subscriber: - class: Drupal\system\EventSubscriber\RouteNormalizerRequestSubscriber - arguments: ['@url_generator', '@path.matcher'] - tags: - - { name: event_subscriber } theme.negotiator.system.batch: class: Drupal\system\Theme\BatchNegotiator arguments: ['@batch.storage', '@request_stack']