diff --git a/core/authorize.php b/core/authorize.php
index fc74bfb..c687ac6 100644
--- a/core/authorize.php
+++ b/core/authorize.php
@@ -20,6 +20,8 @@
  * @link authorize Authorized operation helper functions @endlink
  */
 
+use Symfony\Component\HttpFoundation\Request;
+
 // Change the directory to the Drupal root.
 chdir('..');
 
@@ -68,6 +70,10 @@ function authorize_access_allowed() {
 // variables, however, so we have access to the class autoloader.
 drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
 
+// A request object from the HTTPFoundation to tell us about the request.
+$request = Request::createFromGlobals();
+Drupal::getContainer()->set('request', $request);
+
 // This must go after drupal_bootstrap(), which unsets globals!
 global $conf;
 
@@ -134,7 +140,7 @@ function authorize_access_allowed() {
   }
   // If a batch is running, let it run.
   elseif (isset($_GET['batch'])) {
-    $output = _batch_page();
+    $output = _batch_page($request);
   }
   else {
     if (empty($_SESSION['authorize_operation']) || empty($_SESSION['authorize_filetransfer_info'])) {
diff --git a/core/includes/batch.inc b/core/includes/batch.inc
index ff0b4cb..0bc4f2e 100644
--- a/core/includes/batch.inc
+++ b/core/includes/batch.inc
@@ -15,13 +15,14 @@
  */
 
 use \Symfony\Component\HttpFoundation\JsonResponse;
+use \Symfony\Component\HttpFoundation\Request;
 
 /**
  * Loads a batch from the database.
  *
  * @param $id
  *   The ID of the batch to load. When a progressive batch is being processed,
- *   the relevant ID is found in $_REQUEST['id'].
+ *   the relevant ID is found in $request->get('id').
  *
  * @return
  *   An array representing the batch, or FALSE if no batch was found.
@@ -42,16 +43,16 @@ function batch_load($id) {
  *
  * @see _batch_shutdown()
  */
-function _batch_page() {
+function _batch_page(Request $request) {
   $batch = &batch_get();
 
-  if (!isset($_REQUEST['id'])) {
+  if (!($request_id = $request->get('id'))) {
     return FALSE;
   }
 
   // Retrieve the current state of the batch.
   if (!$batch) {
-    $batch = batch_load($_REQUEST['id']);
+    $batch = batch_load($request_id);
     if (!$batch) {
       drupal_set_message(t('No active batch.'), 'error');
       drupal_goto();
@@ -70,7 +71,7 @@ function _batch_page() {
     }
   }
 
-  $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : '';
+  $op = $request->get('op');
   $output = NULL;
   switch ($op) {
     case 'start':
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 32c0b49..ebb423c 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -604,7 +604,7 @@ function install_run_task($task, &$install_state) {
     // any output from the batch process, until the task is complete.
     elseif ($current_batch == $function) {
       include_once __DIR__ . '/batch.inc';
-      $output = _batch_page();
+      $output = _batch_page(Drupal::request());
       // Because Batch API now returns a JSON response for intermediary steps,
       // but the installer doesn't handle Response objects yet, just send the
       // output here and emulate the old model.
diff --git a/core/modules/system/lib/Drupal/system/Controller/BatchController.php b/core/modules/system/lib/Drupal/system/Controller/BatchController.php
new file mode 100644
index 0000000..7b39650
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Controller/BatchController.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Controller\BatchController.
+ */
+
+namespace Drupal\system\Controller;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+
+
+/**
+ * Controller routines for system routes.
+ */
+class BatchController {
+
+  /**
+   * Returns a system batch page.
+   *
+   * @return mixed
+   *   A Symfony\Component\HttpFoundation\Response object or page element.
+   *
+   */
+  public function batchPage(Request $request) {
+    require_once DRUPAL_ROOT . '/core/includes/batch.inc';
+    $output = _batch_page($request);
+
+    if ($output === FALSE) {
+      throw new AccessDeniedHttpException();
+    }
+    elseif ($output instanceof Response) {
+      return $output;
+    }
+    elseif (isset($output)) {
+      // Force a page without blocks or messages to
+      // display a list of collected messages later.
+      drupal_set_page_content($output);
+      $page = element_info('page');
+      $page['#show_messages'] = FALSE;
+      return $page;
+    }
+  }
+
+}
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 1566c80..40458fd 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -1381,29 +1381,6 @@ function system_php() {
 }
 
 /**
- * Default page callback for batches.
- */
-function system_batch_page() {
-  require_once DRUPAL_ROOT . '/core/includes/batch.inc';
-  $output = _batch_page();
-
-  if ($output === FALSE) {
-    throw new AccessDeniedHttpException();
-  }
-  elseif ($output instanceof Response) {
-    return $output;
-  }
-  elseif (isset($output)) {
-    // Force a page without blocks or messages to
-    // display a list of collected messages later.
-    drupal_set_page_content($output);
-    $page = element_info('page');
-    $page['#show_messages'] = FALSE;
-    return $page;
-  }
-}
-
-/**
  * Returns HTML for an administrative block for display.
  *
  * @param $variables
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 0d472e5..257f424 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -1013,11 +1013,9 @@ function system_menu() {
 
   // Default page for batch operations.
   $items['batch'] = array(
-    'page callback' => 'system_batch_page',
-    'access callback' => TRUE,
+    'route_name' => 'system_batch_page',
     'theme callback' => '_system_batch_theme',
     'type' => MENU_CALLBACK,
-    'file' => 'system.admin.inc',
   );
 
   // Localize date formats.
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index 9859d6a..fc8fbed 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -4,6 +4,7 @@ system.cron:
     _controller: '\Drupal\system\CronController::run'
   requirements:
     _access_system_cron: 'TRUE'
+
 system.machine_name_transliterate:
   pattern: '/machine_name/transliterate'
   defaults:
@@ -87,3 +88,10 @@ date_format_localize_reset:
     _form: '\Drupal\system\Form\DateFormatLocalizeResetForm'
   requirements:
     _permission: 'administer site configuration'
+
+system_batch_page:
+  pattern: '/batch'
+  defaults:
+    _controller: '\Drupal\system\Controller\BatchController::batchPage'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/update.php b/core/update.php
index cee23c4..6f935ec 100644
--- a/core/update.php
+++ b/core/update.php
@@ -396,6 +396,8 @@ function update_check_requirements($skip_warnings = FALSE) {
   }
 }
 
+// @todo Refactor this.
+
 // Some unavoidable errors happen because the database is not yet up-to-date.
 // Our custom error handler is not yet installed, so we just suppress them.
 ini_set('display_errors', FALSE);
@@ -414,11 +416,8 @@ function update_check_requirements($skip_warnings = FALSE) {
 drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
 
 // A request object from the HTTPFoundation to tell us about the request.
-// @todo These two lines were copied from index.php which has its own todo about
-// a change required here. Revisit this when that change has been made.
 $request = Request::createFromGlobals();
-drupal_container()
-  ->set('request', $request);
+Drupal::getContainer()->set('request', $request);
 
 // There can be conflicting 'op' parameters because both update and batch use
 // this parameter name. We need the 'op' coming from a POST request to trump
@@ -518,7 +517,7 @@ function update_check_requirements($skip_warnings = FALSE) {
     // Regular batch ops : defer to batch processing API.
     default:
       update_task_list('run');
-      $output = _batch_page();
+      $output = _batch_page($request);
       break;
   }
 }
