diff --git a/core/core.services.yml b/core/core.services.yml
index b850c9e..642fe78 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -218,6 +218,10 @@ services:
   request:
     class: Symfony\Component\HttpFoundation\Request
     synthetic: true
+  request_info:
+    class: Drupal\Core\Routing\RequestInfo
+    calls:
+      - [setRequest, ['@?request=']]
   request_stack:
     class: Symfony\Component\HttpFoundation\RequestStack
   event_dispatcher:
diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index 767555e..aeb96c9 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -1245,7 +1245,7 @@ function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail =
   // Load the router item corresponding to the current page.
   $request = \Drupal::request();
   $system_path = NULL;
-  if ($route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME)) {
+  if ($route_name = \Drupal::service('request_info')->getRouteName()) {
     // @todo https://drupal.org/node/2068471 is adding support so we can tell
     // if this is called on a 404/403 page.
     $system_path = $request->attributes->get('_system_path');
@@ -1874,7 +1874,7 @@ function menu_local_tasks($level = 0) {
     $data['tabs'] = array();
     $data['actions'] = array();
 
-    $route_name = \Drupal::request()->attributes->get(RouteObjectInterface::ROUTE_NAME);
+    $route_name = \Drupal::service('request_info')->getRouteName();
     if (!empty($route_name)) {
       $manager = \Drupal::service('plugin.manager.menu.local_task');
       $local_tasks = $manager->getTasksBuild($route_name);
@@ -2109,7 +2109,7 @@ function menu_secondary_local_tasks() {
  */
 function menu_get_local_actions() {
   $links = menu_local_tasks();
-  $route_name = Drupal::request()->attributes->get(RouteObjectInterface::ROUTE_NAME);
+  $route_name = \Drupal::service('request_info')->getRouteName();
   $manager = \Drupal::service('plugin.manager.menu.local_action');
   return $manager->getActionsForRoute($route_name) + $links['actions'];
 }
diff --git a/core/lib/Drupal/Core/Controller/DialogController.php b/core/lib/Drupal/Core/Controller/DialogController.php
index 5fda730..02baf80 100644
--- a/core/lib/Drupal/Core/Controller/DialogController.php
+++ b/core/lib/Drupal/Core/Controller/DialogController.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\OpenDialogCommand;
 use Drupal\Core\Page\HtmlPage;
+use Drupal\Core\Routing\RequestInfo;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
@@ -34,16 +35,26 @@ class DialogController {
   protected $titleResolver;
 
   /**
+   * The request info.
+   *
+   * @var \Drupal\Core\Routing\RequestInfo
+   */
+  protected $requestInfo;
+
+  /**
    * Constructs a new DialogController.
    *
    * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
    *   The controller resolver service.
    * @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver
    *   The title resolver.
+   * @param \Drupal\Core\Routing\RequestInfo $request_info
+   *   The request info.
    */
-  public function __construct(ControllerResolverInterface $controller_resolver, TitleResolverInterface $title_resolver) {
+  public function __construct(ControllerResolverInterface $controller_resolver, TitleResolverInterface $title_resolver, RequestInfo $request_info) {
     $this->controllerResolver = $controller_resolver;
     $this->titleResolver = $title_resolver;
+    $this->requestInfo = $request_info;
   }
 
   /**
@@ -96,7 +107,7 @@ public function dialog(Request $request, $_content, $modal = FALSE) {
 
     // @todo Remove use of drupal_get_title() when
     //  http://drupal.org/node/1871596 is in.
-    if (!$title = $this->titleResolver->getTitle($request, $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT))) {
+    if (!$title = $this->titleResolver->getTitle($request, $this->requestInfo->getRouteObject($request))) {
       // @todo Remove use of drupal_get_title() when
       //  http://drupal.org/node/1871596 is in.
       $title = drupal_get_title();
@@ -125,7 +136,7 @@ public function dialog(Request $request, $_content, $modal = FALSE) {
       }
       else {
         // Generate a target based on the route id.
-        $route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME);
+        $route_name = $this->requestInfo->getRouteName($request);
         $target = '#' . drupal_html_id("drupal-dialog-$route_name");
       }
     }
diff --git a/core/lib/Drupal/Core/Routing/RequestInfo.php b/core/lib/Drupal/Core/Routing/RequestInfo.php
new file mode 100644
index 0000000..0663dff
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/RequestInfo.php
@@ -0,0 +1,87 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Routing\RequestInfo.
+ */
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Provides a convenient way to access request information.
+ */
+class RequestInfo {
+
+  /**
+   * The current request.
+   *
+   * @var \Symfony\Component\HttpFoundation\Request
+   */
+  protected $request;
+
+  /**
+   * Sets the request object to use.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request object.
+   */
+  public function setRequest(Request $request) {
+    $this->request = $request;
+  }
+
+  /**
+   * Gets the current request.
+   *
+   * @return \Symfony\Component\HttpFoundation\Request
+   */
+  public function getRequest() {
+    if (!$this->request) {
+      $this->setRequest(\Drupal::request());
+    }
+    return $this->request;
+  }
+
+  /**
+   * Get the route name.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   (optional) The request.
+   *
+   * @return string
+   */
+  public function getRouteName(Request $request = NULL) {
+    $request ?: $this->request;
+    return $request->attributes->get(RouteObjectInterface::ROUTE_NAME);
+  }
+
+  /**
+   * Get the route object.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   (optional) The request.
+   *
+   * @return \Symfony\Component\Routing\Route
+   */
+  public function getRouteObject(Request $request = NULL) {
+    $request ?: $this->request;
+    return $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT);
+  }
+
+  /**
+   * Gets the internal path.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   (optional) The request.
+   *
+   * @return string
+   *   The internal requested path without path aliases.
+   */
+  public function getSystemPath(Request $request = NULL) {
+    $request ?: $this->request;
+    return $request->attributes->get('_system_path');
+  }
+
+}
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index daea82c..652fa4d 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -142,7 +142,7 @@ function block_page_build(&$page) {
 
   // Fetch a list of regions for the current theme.
   $all_regions = system_region_list($theme);
-  if (\Drupal::request()->attributes->get(RouteObjectInterface::ROUTE_NAME) != 'block.admin_demo') {
+  if (\Drupal::service('request_info')->getRouteName() != 'block.admin_demo') {
     // Load all region content assigned via blocks.
     foreach (array_keys($all_regions) as $region) {
       // Assign blocks to region.
