diff --git a/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php b/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php
new file mode 100644
index 0000000..f33b9b0
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\EventSubscriber\RouteSubscriber.
+ */
+
+namespace Drupal\views\EventSubscriber;
+
+use Drupal\Core\Routing\RouteBuildEvent;
+use Drupal\Core\Routing\RoutingEvents;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Defines the routes for a view.
+ */
+class RouteSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Implements \EventSubscriberInterface::getSubscribedEvents().
+   */
+  public static function getSubscribedEvents() {
+    $events[RoutingEvents::DYNAMIC] = 'dynamicRoutes';
+    return $events;
+  }
+
+  /**
+   * Adds routes defined by all views.
+   *
+   * @param \Drupal\Core\Routing\RouteBuildEvent $event
+   *   The route building event.
+   */
+  public function dynamicRoutes(RouteBuildEvent $event) {
+    $collection = $event->getRouteCollection();
+
+    $views = views_get_applicable_views('uses_hook_menu');
+    foreach ($views as $data) {
+      list($view, $display_id) = $data;
+      $view->collectRoutes($display_id, $collection);
+    }
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
index 0c4d150..fac3deb 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
@@ -9,6 +9,7 @@
 
 use Drupal\views\ViewExecutable;
 use \Drupal\views\Plugin\views\PluginBase;
+use Symfony\Component\Routing\RouteCollection;
 
 /**
  * @defgroup views_display_plugins Views display plugins
@@ -2433,6 +2434,17 @@ public function renderEmpty() {
   }
 
   /**
+   * Adds the route entry of a view to the collection.#
+   *
+   * @param \Symfony\Component\Routing\RouteCollection $collection
+   *   A collection of routes that should be registered for this resource.
+   */
+  public function collectRoutes(RouteCollection $collection) {
+
+  }
+
+
+  /**
    * If this display creates a page with a menu item, implement it here.
    */
   public function hookMenu() { return array(); }
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php
index f2846c3..19d4dd0 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php
@@ -92,7 +92,10 @@ public function execute() {
     // And the title, which is much easier.
     drupal_set_title(filter_xss_admin($this->view->getTitle()), PASS_THROUGH);
 
-    return $render;
+    $response = $this->view->getResponse();
+    $response->setContent(drupal_render_page($render));
+
+    return $response;
   }
 
   /**
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
index 8a0a85e..6e9529a 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
@@ -9,6 +9,8 @@
 
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
 
 /**
  * The base display plugin for path/callbacks. This is used for pages and feeds.
@@ -33,6 +35,50 @@ protected function defineOptions() {
   }
 
   /**
+   * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::collectRoutes().
+   */
+  public function collectRoutes(RouteCollection $collection) {
+    $view_id = $this->view->storage->id();
+    $display_id = $this->display['id'];
+
+    // @todo How do we apply argument validation?
+    $bits = explode('/', $this->getOption('path'));
+    // @todo Figure out validation/argument loading.
+    // Replace % with %views_arg for menu autoloading and add to the
+    // page arguments so the argument actually comes through.
+    $arg_counter = 0;
+    foreach ($bits as $pos => &$bit) {
+      if ($bit == '%') {
+        $bit = '{arg' . $arg_counter++ . ' }';
+      }
+    }
+    $route_path = '/' . implode('/', $bits);
+
+    $route = new Route($route_path, array(
+      '_controller' => 'views.page_controller:handle',
+      'view_id' => $view_id,
+      'display_id' => $display_id,
+    ));
+
+    // Add access check parameters to the route.
+    $access_plugin = $this->getPlugin('access');
+    if (!isset($access_plugin)) {
+      // @todo Do we want to support a default plugin in getPlugin itself?
+      $access_plugin = drupal_container()->get('plugin.manager.views.access')->createInstance('none');
+    }
+    $callback = $access_plugin->get_access_callback();
+    if (is_array($callback)) {
+      list($access_callback, $args) = $callback;
+      $route->addOptions(array(
+        '_access_callback' => $access_callback,
+        '_access_arguments' => $args,
+      ));
+    }
+
+    $collection->add("$view_id.$display_id", $route);
+  }
+
+  /**
    * Add this display's path information to Drupal's menu system.
    */
   public function executeHookMenu($callbacks) {
@@ -97,13 +143,13 @@ public function executeHookMenu($callbacks) {
     if ($path) {
       $items[$path] = array(
         // Default views page entry.
-        'page callback' => 'views_page',
-        'page arguments' => $page_arguments,
+        // _menu_router_build() denies access to paths without a page callback.
+        'page callback' => 'NOT_USED',
         // Default access check (per display).
-        'access callback' => 'views_access',
-        'access arguments' => $access_arguments,
+        // 'access callback' => 'views_access',
+        // 'access arguments' => $access_arguments,
         // Identify URL embedded arguments and correlate them to a handler.
-        'load arguments'  => array($this->view->storage->id(), $this->display['id'], '%index'),
+        // 'load arguments'  => array($this->view->storage->id(), $this->display['id'], '%index'),
       );
       $menu = $this->getOption('menu');
       if (empty($menu)) {
@@ -158,14 +204,13 @@ public function executeHookMenu($callbacks) {
             $default_path = implode('/', $bits);
             $items[$default_path] = array(
               // Default views page entry.
-              'page callback' => 'views_page',
-              'page arguments' => $page_arguments,
+              'page callback' => 'NOT_USED',
               // Default access check (per display).
-              'access callback' => 'views_access',
-              'access arguments' => $access_arguments,
+              // 'access callback' => 'views_access',
+              // 'access arguments' => $access_arguments,
               // Identify URL embedded arguments and correlate them to a
               // handler.
-              'load arguments'  => array($this->view->storage->id(), $this->display['id'], '%index'),
+              // 'load arguments'  => array($this->view->storage->id(), $this->display['id'], '%index'),
               'title' => $tab_options['title'],
               'description' => $tab_options['description'],
               'menu_name' => $tab_options['name'],
diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php
index 49ed01c..3f36324 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php
@@ -7,14 +7,16 @@
 
 namespace Drupal\views\Tests\Plugin;
 
-use Drupal\views\Tests\Plugin\PluginTestBase;
+use Drupal\views\Tests\ViewUnitTestBase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
 
 /**
  * Tests the page display plugin.
  *
  * @see Drupal\views\Plugin\display\Page
  */
-class DisplayPageTest extends PluginTestBase {
+class DisplayPageTest extends ViewUnitTestBase {
 
   /**
    * Views used by this test.
@@ -23,6 +25,20 @@ class DisplayPageTest extends PluginTestBase {
    */
   public static $testViews = array('test_page_display');
 
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('system');
+
+  /**
+   * The router dumper to get all routes.
+   *
+   * @var \Drupal\Core\Routing\MatcherDumper
+   */
+  protected $routerDumper;
+
   public static function getInfo() {
     return array(
       'name' => 'Display: Page plugin',
@@ -34,18 +50,37 @@ public static function getInfo() {
   protected function setUp() {
     parent::setUp();
 
-    $this->enableViewsTestModule();
+    // Setup the needed tables in order to make the drupal router working.
+    $this->installSchema('system', 'router');
+    $this->installSchema('system', 'url_alias');
+    $this->installSchema('system', 'menu_router');
   }
 
   /**
    * Checks the behavior of the page for access denied/not found behaviours.
    */
   public function testPageResponses() {
-    $view = views_get_view('test_page_display');
-    $this->drupalGet('test_page_display_403');
-    $this->assertResponse(403);
-    $this->drupalGet('test_page_display_404');
-    $this->assertResponse(404);
+    // @todo Importing a route should fire a container rebuild.
+    $this->container->get('router.builder')->rebuild();
+
+    $subrequest = Request::create('/test_page_display_403', 'GET');
+    $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST);
+    $this->assertEqual($response->getStatusCode(), 403);
+
+    $subrequest = Request::create('/test_page_display_404', 'GET');
+    $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST);
+    $this->assertEqual($response->getStatusCode(), 404);
+
+    $subrequest = Request::create('/test_page_display_200', 'GET');
+    $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST);
+    $this->assertEqual($response->getStatusCode(), 200);
+  }
+
+  /**
+   * Checks that the router items are properly registered
+   */
+  public function testPageRouterItems() {
+    $this->container->get('router.builder')->rebuild();
   }
 
 }
diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php
new file mode 100644
index 0000000..e246ee7
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Tests\ViewPageControllerTest.
+ */
+
+namespace Drupal\views\Tests;
+
+use Drupal\views\ViewPageController;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Tests the page controller but not the actualy execution/rendering of a view.
+ *
+ * @see \Drupal\views\ViewPageController
+ */
+class ViewPageControllerTest extends ViewUnitTestBase {
+
+  /**
+   * Views used by this test.
+   *
+   * @var array
+   */
+  public static $testViews = array('test_page_view');
+
+  /**
+   * The page controller of views.
+   *
+   * @var \Drupal\views\ViewPageController
+   */
+  public $pageController;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'View page controller test',
+      'description' => 'Tests views page controller.',
+      'group' => 'Views'
+    );
+  }
+
+  protected function setUp() {
+    parent::setUp();
+
+    $this->pageController = $this->container->get('views.page_controller');
+  }
+
+
+  /**
+   * Tests the page controller.
+   */
+  public function testPageController() {
+    $this->assertTrue($this->pageController instanceof ViewPageController, 'Ensure the right class is stored in the container');
+
+    // Pass in a non existent view.
+    $random_view_id = $this->randomName();
+    $exception_found = FALSE;
+    try {
+      $this->pageController->handle($random_view_id, 'default');
+    }
+
+    catch (\Exception $e) {
+      $exception_found = TRUE;
+    }
+    $this->assertTrue($exception_found, 'Exception thrown when view was not found');
+
+    $output = $this->pageController->handle('test_page_view', 'default');
+    $this->assertTrue(is_array($output));
+    $this->assertEqual($output['#view']->storage->id, 'test_page_view', 'The right view was executed.');
+
+    $output = $this->pageController->handle('test_page_view', 'page_1');
+    $this->assertTrue($output instanceof Response, 'Ensure the page display returns a response object.');
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewUnitTestBase.php b/core/modules/views/lib/Drupal/views/Tests/ViewUnitTestBase.php
index 334b5da..722104b 100644
--- a/core/modules/views/lib/Drupal/views/Tests/ViewUnitTestBase.php
+++ b/core/modules/views/lib/Drupal/views/Tests/ViewUnitTestBase.php
@@ -24,6 +24,17 @@
  */
 abstract class ViewUnitTestBase extends DrupalUnitTestBase {
 
+  /**
+   * Overrides \Drupal\simpletest\DrupalUnitTestBase::containerBuild().
+   */
+  public function containerBuild($container) {
+    parent::containerBuild($container);
+
+    // @todo This mock router is not usable yet.
+    // $container->register('router.route_provider', 'Drupal\system\Tests\Routing\MockRouteProvider');
+  }
+
+
   protected function setUp() {
     parent::setUp();
 
diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php
index 3a82440..463dfbf 100644
--- a/core/modules/views/lib/Drupal/views/ViewExecutable.php
+++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php
@@ -9,6 +9,7 @@
 
 use Symfony\Component\HttpFoundation\Response;
 use Drupal\views\Plugin\Core\Entity\View;
+use Symfony\Component\Routing\RouteCollection;
 
 /**
  * @defgroup views_objects Objects that represent a View or part of a view
@@ -1458,6 +1459,30 @@ public function attachDisplays() {
   }
 
   /**
+   * Called to collect router entries from the view and the display_handler.
+   *
+   * @param string $display_id
+   *   A display id.
+   * @param \Symfony\Component\Routing\RouteCollection $collection
+   *   A menu callback array passed from views_menu_alter().
+   *
+   * @return bool
+   *   Returns FALSE if the display has not been found, TRUE otherwise.
+   */
+  public function collectRoutes($display_id = NULL, RouteCollection $collection = NULL) {
+
+    // This was probably already called, but it's good to be safe.
+    if (!$this->setDisplay($display_id)) {
+      return FALSE;
+    }
+
+    if (isset($this->display_handler)) {
+      $this->display_handler->collectRoutes($collection);
+      return TRUE;
+    }
+  }
+
+  /**
    * Called to get hook_menu() information from the view and the named display handler.
    *
    * @param $display_id
diff --git a/core/modules/views/lib/Drupal/views/ViewPageController.php b/core/modules/views/lib/Drupal/views/ViewPageController.php
new file mode 100644
index 0000000..f1965ea
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/ViewPageController.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\ViewPageController.
+ */
+
+namespace Drupal\views;
+
+use Drupal\Core\Entity\EntityManager;
+use Symfony\Component\HttpFoundation\Request;
+
+class ViewPageController {
+
+  /**
+   * The entity manager to load a view.
+   *
+   * @var \Drupal\Core\Entity\EntityManager
+   */
+  protected $entityManager;
+
+  /**
+   * Constructs a ViewPageController object.
+   *
+   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   */
+  public function __construct(EntityManager $entity_manager) {
+    $this->entityManager = $entity_manager;
+  }
+
+  /**
+   * Handles a response for a view.
+   *
+   * @param string $view_id
+   *   The ID of the view to execute.
+   * @param string $display_id
+   *   The ID of the display to execute.
+   */
+  public function handle($view_id, $display_id, $arg0 = NULL, $arg1 = NULL, $arg2 = NULL, $arg3 = NULL, $arg4 = NULL, $arg5 = NULL, $arg6 = NULL, $arg7 = NULL) {
+    // @todo: Handle args!!
+    $entities = $this->entityManager->getStorageController('view')->load(array($view_id));
+    $entity = reset($entities);
+    if (empty($entity)) {
+      throw new \Exception(format_string('Page controller for view %id requested, but view was not found.', array('%id' => $view_id)));
+    }
+    $view = $entity->get('executable');
+    $func_args = func_get_args();
+    // Drop view and display ID.
+    array_shift($func_args);
+    array_shift($func_args);
+    $args = array_filter($func_args, function($item) {
+      return $item !== NULL;
+    });
+
+    return $view->executeDisplay($display_id, $args);
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/ViewsAccessCheck.php b/core/modules/views/lib/Drupal/views/ViewsAccessCheck.php
new file mode 100644
index 0000000..15e5f43
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/ViewsAccessCheck.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\ViewsAccessCheck.
+ */
+
+namespace Drupal\views;
+
+use Drupal\Core\Access\AccessCheckInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Defines a route access checker.
+ */
+class ViewsAccessCheck implements AccessCheckInterface {
+
+  /**
+   * Implements \Drupal\Core\Access\AccessCheckInterface::applies().
+   */
+  public function applies(Route $route) {
+    return $route->getDefault('view_id') && $route->getDefault('display_id');
+  }
+
+  /**
+   * Implements \Drupal\Core\Access\AccessCheckInterface::applies().
+   */
+  public function access(Route $route, Request $request) {
+    $access_callback = $route->getOption('access_callback');
+    $access_arguments = $route->getOption('access_arguments');
+
+    if (is_callable($access_callback)) {
+      return call_user_func($access_callback, $access_arguments);
+    }
+    // If there is no access check for the view, allow it.
+    return TRUE;
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/ViewsBundle.php b/core/modules/views/lib/Drupal/views/ViewsBundle.php
index dfb6dd6..1a737b5 100644
--- a/core/modules/views/lib/Drupal/views/ViewsBundle.php
+++ b/core/modules/views/lib/Drupal/views/ViewsBundle.php
@@ -37,6 +37,16 @@ public function build(ContainerBuilder $container) {
       ->addArgument(new Reference('config.factory'));
 
     $container->register('views.executable', 'Drupal\views\ViewExecutableFactory');
+
+    $container->register('views.page_controller', 'Drupal\views\ViewPageController')
+      ->addArgument(new Reference('plugin.manager.entity'));
+
+    $container->register('views.route_subscriber', 'Drupal\views\EventSubscriber\RouteSubscriber')
+      ->addArgument(new Reference('config.factory'))
+      ->addTag('event_subscriber');
+
+    $container->register('views.route_access_check', 'Drupal\views\ViewsAccessCheck')
+      ->addTag('access_check');
   }
 
 }
diff --git a/core/modules/views/tests/views_test_config/test_views/views.view.test_page_display.yml b/core/modules/views/tests/views_test_config/test_views/views.view.test_page_display.yml
index 0027a58..e7cb158 100644
--- a/core/modules/views/tests/views_test_config/test_views/views.view.test_page_display.yml
+++ b/core/modules/views/tests/views_test_config/test_views/views.view.test_page_display.yml
@@ -1,14 +1,22 @@
-base_table: node
+base_table: views_test_data
 core: '8'
 description: ''
 disabled: '0'
 display:
   default:
     display_options:
-      access:
-        type: none
-      cache:
-        type: none
+      defaults:
+        fields: '0'
+        pager: '0'
+        pager_options: '0'
+        sorts: '0'
+      fields:
+        age:
+          field: age
+          id: age
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
     display_plugin: default
     display_title: Master
     id: default
@@ -27,6 +35,13 @@ display:
     display_title: Page
     id: page_2
     position: '0'
+  page_3:
+    display_options:
+      path: test_page_display_200
+    display_plugin: page
+    display_title: Page
+    id: page_3
+    position: '0'
 human_name: ''
 id: test_page_display
 tag: ''
diff --git a/core/modules/views/tests/views_test_config/test_views/views.view.test_page_view.yml b/core/modules/views/tests/views_test_config/test_views/views.view.test_page_view.yml
new file mode 100644
index 0000000..41d09e5
--- /dev/null
+++ b/core/modules/views/tests/views_test_config/test_views/views.view.test_page_view.yml
@@ -0,0 +1,30 @@
+base_table: views_test_data
+core: '8'
+description: ''
+disabled: '0'
+display:
+  default:
+    display_options:
+      defaults:
+        fields: '0'
+        pager: '0'
+        pager_options: '0'
+        sorts: '0'
+      fields:
+        age:
+          field: age
+          id: age
+          relationship: none
+          table: views_test_data
+          plugin_id: numeric
+    display_plugin: default
+    display_title: Master
+    id: default
+    position: '0'
+  page_1:
+    display_plugin: page
+    display_title: Test page view
+    id: page_1
+human_name: ''
+id: test_page_view
+tag: ''
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index dc565b8..842377a 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -695,6 +695,12 @@ function views_invalidate_cache() {
   // Set the menu as needed to be rebuilt.
   state()->set('menu_rebuild_needed', TRUE);
 
+  // Set the router to be rebuild.
+  // @todo This isn't a good fix.
+  if (db_table_exists('router')) {
+    drupal_container()->get('router.builder')->rebuild();
+  }
+
   // Invalidate the block cache to update views block derivatives.
   if (module_exists('block')) {
     drupal_container()->get('plugin.manager.block')->clearCachedDefinitions();
