diff --git a/core/modules/aggregator/aggregator.admin.inc b/core/modules/aggregator/aggregator.admin.inc
index 84643bbf7b4c4a2802dc1207a7902ffbf5c7497e..f26d4ff9bb5b89b63184d64ae26e4004088662ed 100644
--- a/core/modules/aggregator/aggregator.admin.inc
+++ b/core/modules/aggregator/aggregator.admin.inc
@@ -8,27 +8,6 @@
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Drupal\aggregator\Plugin\Core\Entity\Feed;
 
-/**
- * Page callback: Refreshes a feed, then redirects to the overview page.
- *
- * @param \Drupal\aggregator\Plugin\Core\Entity\Feed $feed
- *   An object describing the feed to be refreshed.
- *
- * @see aggregator_menu()
- */
-function aggregator_admin_refresh_feed(Feed $feed) {
-  // @todo CSRF tokens are validated in page callbacks rather than access
-  //   callbacks, because access callbacks are also invoked during menu link
-  //   generation. Add token support to routing: http://drupal.org/node/755584.
-  $token = drupal_container()->get('request')->query->get('token');
-  if (!isset($token) || !drupal_valid_token($token, 'aggregator/update/' . $feed->id())) {
-    throw new AccessDeniedHttpException();
-  }
-
-  aggregator_refresh($feed);
-  drupal_goto('admin/config/services/aggregator');
-}
-
 /**
  * Form constructor to add/edit/delete aggregator categories.
  *
diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index 25265f430ae14c17d59b8b0ec73803f2cc7c51d0..5021e57985efd6513db4ed9531639a0c93d052a1 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -119,10 +119,7 @@ function aggregator_menu() {
   );
   $items['admin/config/services/aggregator/update/%aggregator_feed'] = array(
     'title' => 'Update items',
-    'page callback' => 'aggregator_admin_refresh_feed',
-    'page arguments' => array(5),
-    'access arguments' => array('administer news feeds'),
-    'file' => 'aggregator.admin.inc',
+    'route_name' => 'aggregator_feed_refresh',
   );
   $items['admin/config/services/aggregator/list'] = array(
     'title' => 'List',
diff --git a/core/modules/aggregator/aggregator.routing.yml b/core/modules/aggregator/aggregator.routing.yml
index c41cf0ed46c8015de370e1cd6ddae2c3ba8c51b9..2b6864627a4d65b54d3764f07e3a866efe7a5d70 100644
--- a/core/modules/aggregator/aggregator.routing.yml
+++ b/core/modules/aggregator/aggregator.routing.yml
@@ -29,7 +29,14 @@ aggregator_feed_delete:
 aggregator_feed_add:
   pattern: '/admin/config/services/aggregator/add/feed'
   defaults:
-    _controller: '\Drupal\aggregator\Routing\AggregatorController::feedAdd'
+    _content: '\Drupal\aggregator\Routing\AggregatorController::feedAdd'
+  requirements:
+    _permission: 'administer news feeds'
+
+aggregator_feed_refresh:
+  pattern: '/admin/config/services/aggregator/update/{aggregator_feed}'
+  defaults:
+    _controller: '\Drupal\aggregator\Routing\AggregatorController::feedRefresh'
   requirements:
     _permission: 'administer news feeds'
 
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Routing/AggregatorController.php b/core/modules/aggregator/lib/Drupal/aggregator/Routing/AggregatorController.php
index ff2b22265db6ab9b514ef95eb4d2cd88da46057b..dfed7fd84c5951e903ab08b2f6d9d30fe770ba3a 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Routing/AggregatorController.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Routing/AggregatorController.php
@@ -8,9 +8,13 @@
 namespace Drupal\aggregator\Routing;
 
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Drupal\Core\ControllerInterface;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Entity\EntityManager;
+use Drupal\aggregator\FeedInterface;
 
 /**
  * Returns responses for aggregator module routes.
@@ -70,6 +74,34 @@ public function feedAdd() {
     return entity_get_form($feed);
   }
 
+  /**
+   * Refreshes a feed, then redirects to the overview page.
+   *
+   * @param \Drupal\aggregator\FeedInterface $aggregator_feed
+   *   An object describing the feed to be refreshed.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The current request object containing the search string.
+   *
+   * @return \Symfony\Component\HttpFoundation\RedirectResponse
+   *   A redirection to the admin overview page.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+   *   If the query token is missing or invalid.
+   */
+  public function feedRefresh(FeedInterface $aggregator_feed, Request $request) {
+    // @todo CSRF tokens are validated in page callbacks rather than access
+    //   callbacks, because access callbacks are also invoked during menu link
+    //   generation. Add token support to routing: http://drupal.org/node/755584.
+    $token = $request->query->get('token');
+    if (!isset($token) || !drupal_valid_token($token, 'aggregator/update/' . $aggregator_feed->id())) {
+      throw new AccessDeniedHttpException();
+    }
+
+    // @todo after https://drupal.org/node/1972246 find a new place for it.
+    aggregator_refresh($aggregator_feed);
+    return new RedirectResponse(url('admin/config/services/aggregator', array('absolute' => TRUE)));
+  }
+
   /**
    * Displays the aggregator administration page.
    *