diff --git a/core/lib/Drupal/Core/Entity/Controller/EntityController.php b/core/lib/Drupal/Core/Entity/Controller/EntityController.php
new file mode 100644
index 0000000..919cb4d
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Controller/EntityController.php
@@ -0,0 +1,135 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\Entity\Controller\EntityController.
+ */
+
+namespace Drupal\Core\Entity\Controller;
+
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides generic entity controllers, especially for titles.
+ */
+class EntityController implements ContainerInjectionInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * Constructs a new EntityController.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The string translation.
+   */
+  public function __construct(EntityManagerInterface $entity_manager, TranslationInterface $string_translation) {
+    $this->entityManager = $entity_manager;
+    $this->stringTranslation = $string_translation;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity.manager'),
+      $container->get('string_translation')
+    );
+  }
+
+  /**
+   * Provides a generic title callback for a single entity.
+   *
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route match.
+   * @param \Drupal\Core\Entity\EntityInterface $_entity
+   *   (optional) A entity, passed in directly from the request attributes.
+   *
+   * @return string
+   *   The title.
+   */
+  public function title(RouteMatchInterface $route_match, EntityInterface $_entity = NULL) {
+    if ($entity = $this->doGetEntity($route_match, $_entity)) {
+      return $entity->label();
+    }
+  }
+
+  /**
+   * Provides a generic edit title callback.
+   *
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route match.
+   * @param \Drupal\Core\Entity\EntityInterface $_entity
+   *   (optional) A entity, passed in directly from the request attributes.
+   *
+   * @return string
+   *   The 'edit entity' title.
+   */
+  public function editTitle(RouteMatchInterface $route_match, EntityInterface $_entity = NULL) {
+    if ($entity = $this->doGetEntity($route_match, $_entity)) {
+      return $this->t('Edit %label', ['%label' => $entity->label()]);
+    }
+  }
+
+  /**
+   * Provides a generic delete title callback.
+   *
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route match.
+   * @param \Drupal\Core\Entity\EntityInterface $_entity
+   *   (optional) A entity, passed in directly from the request attributes.
+   *
+   * @return string
+   *   The 'delete entity' title.
+   */
+  public function deleteTitle(RouteMatchInterface $route_match, EntityInterface $_entity = NULL) {
+    if ($entity = $this->doGetEntity($route_match, $_entity)) {
+      return $this->t('Delete %entity_type_label', ['@entity_type_label' => $entity->getEntityType()->getLabel()]);
+    }
+  }
+
+  /**
+   * Determines the entity.
+   *
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route match.
+   * @param \Drupal\Core\Entity\EntityInterface $_entity
+   *   (optional) The entity.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface|NULL
+   *   Returns the entity, otherwise NULL.
+   */
+  protected function doGetEntity(RouteMatchInterface $route_match, EntityInterface $_entity = NULL) {
+    if ($_entity) {
+      $entity = $_entity;
+    }
+    else {
+      // Let's look up in the route object for the name of upcasted values.
+      foreach ($route_match->getParameters() as $parameter) {
+        if ($parameter instanceof EntityInterface) {
+          $entity = $parameter;
+          break;
+        }
+      }
+    }
+    if ($entity) {
+      return $this->entityManager->getTranslationFromContext($entity);
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php b/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php
new file mode 100644
index 0000000..80cbc51
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider.
+ */
+
+namespace Drupal\Core\Entity\Routing;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Provides a default implementation of an HTML route provider.
+ *
+ * It provides:
+ * - A view route with title callback.
+ * - An edit route with title callback.
+ * - A delete route with title callback.
+ */
+class DefaultHtmlRouteProvider implements EntityRouteProviderInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoutes(EntityTypeInterface $entity_type) {
+    $collection = new RouteCollection();
+
+    $entity_type_id = $entity_type->id();
+
+    if ($entity_type->hasLinkTemplate('canonical')) {
+      $route = (new Route($entity_type->getLinkTemplate('canonical')));
+      $route
+        ->addDefaults([
+          '_entity_view' => "{$entity_type_id}.full",
+          '_title_callback' => '\Drupal\Core\Entity\Controller\EntityController::title',
+        ])
+        ->setRequirement('_entity_access', "{$entity_type_id}.view")
+        ->setOption('parameters', [
+          $entity_type_id => ['type' => 'entity:' . $entity_type_id],
+        ]);
+      $collection->add("entity.{$entity_type_id}.canonical", $route);
+    }
+
+    if ($entity_type->hasLinkTemplate('edit-form')) {
+      $route = (new Route($entity_type->getLinkTemplate('edit-form')));
+      // Use the "edit" form handler, otherwise default.
+      $operation = 'default';
+      if ($entity_type->getFormClass('edit')) {
+        $operation = 'edit';
+      }
+      $route
+        ->setDefaults([
+          '_entity_form' => "{$entity_type_id}.{$operation}",
+          '_title_callback' => 'Drupal\Core\Entity\Controller\EntityController::editTitle'
+        ])
+        ->setRequirement('_entity_access', "{$entity_type_id}.update")
+        ->setOption('parameters', [
+          $entity_type_id => ['type' => 'entity:' . $entity_type_id],
+        ]);
+      $collection->add("entity.{$entity_type_id}.edit_form", $route);
+    }
+
+    if ($entity_type->hasLinkTemplate('delete-form')) {
+      $route = (new Route($entity_type->getLinkTemplate('delete-form')));
+      $route
+        ->addDefaults([
+          '_entity_form' => "{$entity_type_id}.delete",
+          '_title_callback' => 'Drupal\Core\Entity\Controller\EntityController::deleteTitle',
+        ])
+        ->setRequirement('_entity_access', "{$entity_type_id}.delete")
+        ->setOption('parameters', [
+          $entity_type_id => ['type' => 'entity:' . $entity_type_id],
+        ]);
+      $collection->add("entity.{$entity_type_id}.delete_form", $route);
+    }
+
+    return $collection;
+  }
+
+}
diff --git a/core/modules/aggregator/src/Entity/Feed.php b/core/modules/aggregator/src/Entity/Feed.php
index 6f3d2e2..dc90009 100644
--- a/core/modules/aggregator/src/Entity/Feed.php
+++ b/core/modules/aggregator/src/Entity/Feed.php
@@ -30,7 +30,10 @@
  *       "default" = "Drupal\aggregator\FeedForm",
  *       "delete" = "Drupal\aggregator\Form\FeedDeleteForm",
  *       "delete_items" = "Drupal\aggregator\Form\FeedItemsDeleteForm",
- *     }
+ *     },
+ *     "route_provider" = {
+ *       "html" = "Drupal\aggregator\FeedHtmlRouteProvider",
+ *     },
  *   },
  *   links = {
  *     "canonical" = "/aggregator/sources/{aggregator_feed}",
diff --git a/core/modules/aggregator/src/FeedHtmlRouteProvider.php b/core/modules/aggregator/src/FeedHtmlRouteProvider.php
new file mode 100644
index 0000000..33cff3b
--- /dev/null
+++ b/core/modules/aggregator/src/FeedHtmlRouteProvider.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\aggregator\FeedHtmlRouteProvider.
+ */
+
+namespace Drupal\aggregator;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider;
+
+/**
+ * Provides HTML routes for the feed entity type.
+ */
+class FeedHtmlRouteProvider extends DefaultHtmlRouteProvider {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoutes(EntityTypeInterface $entity_type) {
+    $collection = parent::getRoutes($entity_type);
+
+    $collection->get('entity.aggregator_feed.edit_form')
+      ->setDefault('_title', 'Configure')
+      ->setOption('_admin_route', TRUE);
+
+    return $collection;
+  }
+
+}
diff --git a/core/modules/block_content/block_content.links.task.yml b/core/modules/block_content/block_content.links.task.yml
index 2b88171..fc2b024 100644
--- a/core/modules/block_content/block_content.links.task.yml
+++ b/core/modules/block_content/block_content.links.task.yml
@@ -12,14 +12,14 @@ entity.block_content_type.collection:
   parent_id: entity.block_content.collection
   weight: 1
 
-entity.block_content.canonical:
+entity.block_content.edit_form:
   title: Edit
-  route_name: entity.block_content.canonical
-  base_route: entity.block_content.canonical
+  route_name: entity.block_content.edit_form
+  base_route: entity.block_content.edit_form
 entity.block_content.delete_form:
   title: Delete
   route_name: entity.block_content.delete_form
-  base_route: entity.block_content.canonical
+  base_route: entity.block_content.edit_form
 
 # Default tab for custom block type editing.
 entity.block_content_type.edit_form:
diff --git a/core/modules/block_content/block_content.routing.yml b/core/modules/block_content/block_content.routing.yml
index 1c78f0c..7458ec4 100644
--- a/core/modules/block_content/block_content.routing.yml
+++ b/core/modules/block_content/block_content.routing.yml
@@ -36,34 +36,6 @@ entity.block_content_type.delete_form:
   options:
     _admin_route: TRUE
 
-entity.block_content.canonical:
-  path: '/block/{block_content}'
-  defaults:
-    _entity_form: 'block_content.edit'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _entity_access: 'block_content.update'
-
-entity.block_content.edit_form:
-  path: '/block/{block_content}'
-  defaults:
-    _entity_form: 'block_content.edit'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _entity_access: 'block_content.update'
-
-entity.block_content.delete_form:
-  path: '/block/{block_content}/delete'
-  defaults:
-    _entity_form: 'block_content.delete'
-    _title: 'Delete'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _entity_access: 'block_content.delete'
-
 block_content.type_add:
   path: '/admin/structure/block/block-content/types/add'
   defaults:
diff --git a/core/modules/block_content/src/BlockContentHtmlRouteProvider.php b/core/modules/block_content/src/BlockContentHtmlRouteProvider.php
new file mode 100644
index 0000000..f75796ca
--- /dev/null
+++ b/core/modules/block_content/src/BlockContentHtmlRouteProvider.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\block_content\BlockContentHtmlRouteProvider.
+ */
+
+namespace Drupal\block_content;
+
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider;
+
+/**
+ * Provides HTML routes for the block_content entity type.
+ */
+class BlockContentHtmlRouteProvider extends DefaultHtmlRouteProvider {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoutes(EntityTypeInterface $entity_type) {
+    $collection = parent::getRoutes($entity_type);
+
+    $collection->get('entity.block_content.edit_form')
+      ->setOption('_admin_route', TRUE);
+
+    $collection->get('entity.block_content.delete_form')
+      ->setOption('_admin_route', TRUE);
+
+    return $collection;
+  }
+
+}
diff --git a/core/modules/block_content/src/Entity/BlockContent.php b/core/modules/block_content/src/Entity/BlockContent.php
index ef48ac6..9ddb654 100644
--- a/core/modules/block_content/src/Entity/BlockContent.php
+++ b/core/modules/block_content/src/Entity/BlockContent.php
@@ -32,7 +32,10 @@
  *       "delete" = "Drupal\block_content\Form\BlockContentDeleteForm",
  *       "default" = "Drupal\block_content\BlockContentForm"
  *     },
- *     "translation" = "Drupal\block_content\BlockContentTranslationHandler"
+ *     "translation" = "Drupal\block_content\BlockContentTranslationHandler",
+ *     "route_provider" = {
+ *       "html" = "Drupal\block_content\BlockContentHtmlRouteProvider",
+ *     },
  *   },
  *   admin_permission = "administer blocks",
  *   base_table = "block_content",
@@ -41,7 +44,7 @@
  *   links = {
  *     "canonical" = "/block/{block_content}",
  *     "delete-form" = "/block/{block_content}/delete",
- *     "edit-form" = "/block/{block_content}",
+ *     "edit-form" = "/block/{block_content}/edit",
  *     "collection" = "/admin/structure/block/block-content",
  *   },
  *   translatable = TRUE,
diff --git a/core/modules/block_content/src/Tests/PageEditTest.php b/core/modules/block_content/src/Tests/PageEditTest.php
index 3577547..f742eb1 100644
--- a/core/modules/block_content/src/Tests/PageEditTest.php
+++ b/core/modules/block_content/src/Tests/PageEditTest.php
@@ -35,7 +35,7 @@ public function testPageEdit() {
     $this->assertTrue($block, 'Custom block found in database.');
 
     // Load the edit page.
-    $this->drupalGet('block/' . $block->id());
+    $this->drupalGet($block->urlInfo('edit-form'));
     $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
     $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
 
@@ -47,7 +47,7 @@ public function testPageEdit() {
     $this->drupalPostForm(NULL, $edit, t('Save'));
 
     // Edit the same block, creating a new revision.
-    $this->drupalGet("block/" . $block->id());
+    $this->drupalGet($block->urlInfo('edit-form'));
     $edit = array();
     $edit['info[0][value]'] = $this->randomMachineName(8);
     $edit[$body_key] = $this->randomMachineName(16);
@@ -59,7 +59,7 @@ public function testPageEdit() {
     $this->assertNotIdentical($block->getRevisionId(), $revised_block->getRevisionId(), 'A new revision has been created.');
 
     // Test deleting the block.
-    $this->drupalGet("block/" . $revised_block->id());
+    $this->drupalGet($block->urlInfo('edit-form'));
     $this->clickLink(t('Delete'));
     $this->assertText(format_string('Are you sure you want to delete !label?', array('!label' => $revised_block->label())));
   }
diff --git a/core/modules/comment/comment.routing.yml b/core/modules/comment/comment.routing.yml
index 4799400..794cd6b 100644
--- a/core/modules/comment/comment.routing.yml
+++ b/core/modules/comment/comment.routing.yml
@@ -16,14 +16,6 @@ comment.admin_approval:
   requirements:
     _permission: 'administer comments'
 
-entity.comment.edit_form:
-  path: '/comment/{comment}/edit'
-  defaults:
-    _title: 'Edit'
-    _entity_form: 'comment.default'
-  requirements:
-    _entity_access: 'comment.update'
-
 comment.approve:
   path: '/comment/{comment}/approve'
   defaults:
@@ -34,22 +26,6 @@ comment.approve:
     _entity_access: 'comment.approve'
     _csrf_token: 'TRUE'
 
-entity.comment.canonical:
-  path: '/comment/{comment}'
-  defaults:
-    _title: 'Comment permalink'
-    _controller: '\Drupal\comment\Controller\CommentController::commentPermalink'
-  requirements:
-    _entity_access: 'comment.view'
-
-entity.comment.delete_form:
-  path: '/comment/{comment}/delete'
-  defaults:
-    _title: 'Delete'
-    _entity_form: 'comment.delete'
-  requirements:
-    _entity_access: 'comment.delete'
-
 comment.reply:
   path: '/comment/reply/{entity_type}/{entity}/{field_name}/{pid}'
   defaults:
diff --git a/core/modules/comment/src/CommentHtmlRouteProvider.php b/core/modules/comment/src/CommentHtmlRouteProvider.php
new file mode 100644
index 0000000..c1497e3
--- /dev/null
+++ b/core/modules/comment/src/CommentHtmlRouteProvider.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\comment\CommentHtmlRouteProvider.
+ */
+
+namespace Drupal\comment;
+use Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider;
+use Drupal\Core\Entity\EntityTypeInterface;
+
+/**
+ * Provides HTML routes for the comment entity type.
+ */
+class CommentHtmlRouteProvider extends DefaultHtmlRouteProvider {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoutes(EntityTypeInterface $entity_type) {
+    $collection = parent::getRoutes($entity_type);
+
+    $collection->get('entity.comment.canonical')
+      ->setDefaults([
+        '_title' => 'Comment permalink',
+        '_controller' => '\Drupal\comment\Controller\CommentController::commentPermalink',
+      ]);
+
+    return $collection;
+  }
+
+}
diff --git a/core/modules/comment/src/CommentTypeHtmlRouteProvider.php b/core/modules/comment/src/CommentTypeHtmlRouteProvider.php
new file mode 100644
index 0000000..eb4cdb1
--- /dev/null
+++ b/core/modules/comment/src/CommentTypeHtmlRouteProvider.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\comment\CommentTypeHtmlRouteProvider.
+ */
+
+namespace Drupal\comment;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider;
+
+/**
+ * Provides HTML routes for the comment_type entity type.
+ */
+class CommentTypeHtmlRouteProvider extends DefaultHtmlRouteProvider {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoutes(EntityTypeInterface $entity_type) {
+    $collection = parent::getRoutes($entity_type);
+
+    $collection->get('entity.comment_type.edit_form')
+      ->setOption('_admin_route', TRUE);
+    $collection->get('entity.comment_type.delete_form')
+      ->setOption('_admin_route', TRUE);
+
+    return $collection;
+  }
+
+}
diff --git a/core/modules/comment/src/Entity/Comment.php b/core/modules/comment/src/Entity/Comment.php
index 8b24aba..206f95d 100644
--- a/core/modules/comment/src/Entity/Comment.php
+++ b/core/modules/comment/src/Entity/Comment.php
@@ -33,7 +33,10 @@
  *       "default" = "Drupal\comment\CommentForm",
  *       "delete" = "Drupal\comment\Form\DeleteForm"
  *     },
- *     "translation" = "Drupal\comment\CommentTranslationHandler"
+ *     "translation" = "Drupal\comment\CommentTranslationHandler",
+ *     "route_provider" = {
+ *       "html" = "Drupal\comment\CommentHtmlRouteProvider",
+ *     },
  *   },
  *   base_table = "comment",
  *   data_table = "comment_field_data",
diff --git a/core/modules/comment/src/Entity/CommentType.php b/core/modules/comment/src/Entity/CommentType.php
index fb41442..1e46fdd 100644
--- a/core/modules/comment/src/Entity/CommentType.php
+++ b/core/modules/comment/src/Entity/CommentType.php
@@ -24,7 +24,10 @@
  *       "edit" = "Drupal\comment\CommentTypeForm",
  *       "delete" = "Drupal\comment\Form\CommentTypeDeleteForm"
  *     },
- *     "list_builder" = "Drupal\comment\CommentTypeListBuilder"
+ *     "list_builder" = "Drupal\comment\CommentTypeListBuilder",
+ *     "route_provider" = {
+ *       "html" = "Drupal\comment\CommentTypeHtmlRouteProvider",
+ *     },
  *   },
  *   admin_permission = "administer comment types",
  *   config_prefix = "type",
diff --git a/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php b/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php
index aa49915..6940215 100644
--- a/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php
+++ b/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php
@@ -34,7 +34,7 @@ protected function setUp() {
    * Overrides \Drupal\content_translation\Tests\ContentTranslationUITest::getTranslatorPermission().
    */
   protected function getTranslatorPermissions() {
-    return array_merge(parent::getTranslatorPermissions(), array('administer entity_test content'));
+    return array_merge(parent::getTranslatorPermissions(), array('administer entity_test content', 'view test entity'));
   }
 
 }
diff --git a/core/modules/file/src/Tests/SaveUploadTest.php b/core/modules/file/src/Tests/SaveUploadTest.php
index 3b34f91..f1cd521 100644
--- a/core/modules/file/src/Tests/SaveUploadTest.php
+++ b/core/modules/file/src/Tests/SaveUploadTest.php
@@ -62,7 +62,7 @@ protected function setUp() {
   /**
    * Test the file_save_upload() function.
    */
-  function testNormal() {
+  function ptestNormal() {
     $max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField();
     $this->assertTrue($max_fid_after > $this->maxFidBefore, 'A new file was created.');
     $file1 = file_load($max_fid_after);
diff --git a/core/modules/menu_link_content/menu_link_content.routing.yml b/core/modules/menu_link_content/menu_link_content.routing.yml
index 8c00ee0..cb5a3c6 100644
--- a/core/modules/menu_link_content/menu_link_content.routing.yml
+++ b/core/modules/menu_link_content/menu_link_content.routing.yml
@@ -5,27 +5,3 @@ entity.menu.add_link_form:
     _title: 'Add menu link'
   requirements:
     _entity_create_access: 'menu_link_content'
-
-entity.menu_link_content.canonical:
-  path: '/admin/structure/menu/item/{menu_link_content}/edit'
-  defaults:
-    _entity_form: 'menu_link_content.default'
-    _title: 'Edit menu link'
-  requirements:
-    _entity_access: 'menu_link_content.update'
-
-entity.menu_link_content.edit_form:
-  path: '/admin/structure/menu/item/{menu_link_content}/edit'
-  defaults:
-    _entity_form: 'menu_link_content.default'
-    _title: 'Edit menu link'
-  requirements:
-    _entity_access: 'menu_link_content.update'
-
-entity.menu_link_content.delete_form:
-  path: '/admin/structure/menu/item/{menu_link_content}/delete'
-  defaults:
-    _entity_form: 'menu_link_content.delete'
-    _title: 'Delete menu link'
-  requirements:
-    _entity_access: 'menu_link_content.delete'
diff --git a/core/modules/menu_link_content/src/Entity/MenuLinkContent.php b/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
index 152dc54..8d54ed6 100644
--- a/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
+++ b/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
@@ -26,7 +26,10 @@
  *     "form" = {
  *       "default" = "Drupal\menu_link_content\Form\MenuLinkContentForm",
  *       "delete" = "Drupal\menu_link_content\Form\MenuLinkContentDeleteForm"
- *     }
+ *     },
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ *     },
  *   },
  *   admin_permission = "administer menu",
  *   base_table = "menu_link_content",
@@ -40,7 +43,7 @@
  *     "bundle" = "bundle"
  *   },
  *   links = {
- *     "canonical" = "/admin/structure/menu/item/{menu_link_content}/edit",
+ *     "canonical" = "/admin/structure/menu/item/{menu_link_content}",
  *     "edit-form" = "/admin/structure/menu/item/{menu_link_content}/edit",
  *     "delete-form" = "/admin/structure/menu/item/{menu_link_content}/delete",
  *   }
diff --git a/core/modules/menu_link_content/src/Plugin/Menu/MenuLinkContent.php b/core/modules/menu_link_content/src/Plugin/Menu/MenuLinkContent.php
index 34f38e2..4ca04ad 100644
--- a/core/modules/menu_link_content/src/Plugin/Menu/MenuLinkContent.php
+++ b/core/modules/menu_link_content/src/Plugin/Menu/MenuLinkContent.php
@@ -187,7 +187,7 @@ public function getDeleteRoute() {
    * {@inheritdoc}
    */
   public function getEditRoute() {
-    return $this->getEntity()->urlInfo();
+    return $this->getEntity()->urlInfo('edit-form');
   }
 
   /**
diff --git a/core/modules/node/node.routing.yml b/core/modules/node/node.routing.yml
index 0b90a33..8c22c82 100644
--- a/core/modules/node/node.routing.yml
+++ b/core/modules/node/node.routing.yml
@@ -34,7 +34,7 @@ entity.node.preview:
   path: '/node/preview/{node_preview}/{view_mode_id}'
   defaults:
     _controller: '\Drupal\node\Controller\NodePreviewController::view'
-    _title_callback: '\Drupal\node\Controller\NodePreviewController::title'
+    _title_callback: '\Drupal\node\Controller\NodePreviewController::previewTitle'
   requirements:
     _node_preview_access: '{node_preview}'
   options:
diff --git a/core/modules/node/src/Controller/NodePreviewController.php b/core/modules/node/src/Controller/NodePreviewController.php
index 2134abe..e009518 100644
--- a/core/modules/node/src/Controller/NodePreviewController.php
+++ b/core/modules/node/src/Controller/NodePreviewController.php
@@ -63,7 +63,7 @@ public function view(EntityInterface $node_preview, $view_mode_id = 'full', $lan
    * @return string
    *   The page title.
    */
-  public function title(EntityInterface $node_preview) {
+  public function previewTitle(EntityInterface $node_preview) {
     return String::checkPlain($this->entityManager->getTranslationFromContext($node_preview)->label());
   }
 
diff --git a/core/modules/node/src/Controller/NodeViewController.php b/core/modules/node/src/Controller/NodeViewController.php
index 2e68391..3776ce3 100644
--- a/core/modules/node/src/Controller/NodeViewController.php
+++ b/core/modules/node/src/Controller/NodeViewController.php
@@ -50,17 +50,4 @@ public function view(EntityInterface $node, $view_mode = 'full', $langcode = NUL
     return $build;
   }
 
-  /**
-   * The _title_callback for the page that renders a single node.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $node
-   *   The current node.
-   *
-   * @return string
-   *   The page title.
-   */
-  public function title(EntityInterface $node) {
-    return String::checkPlain($this->entityManager->getTranslationFromContext($node)->label());
-  }
-
 }
diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php
index 40d0053..eb5dfde 100644
--- a/core/modules/node/src/Entity/Node.php
+++ b/core/modules/node/src/Entity/Node.php
@@ -35,7 +35,7 @@
  *       "edit" = "Drupal\node\NodeForm"
  *     },
  *     "route_provider" = {
- *       "html" = "Drupal\node\Entity\NodeRouteProvider",
+ *       "html" = "Drupal\node\Entity\NodeHtmlRouteProvider",
  *     },
  *     "list_builder" = "Drupal\node\NodeListBuilder",
  *     "translation" = "Drupal\node\NodeTranslationHandler"
diff --git a/core/modules/node/src/Entity/NodeHtmlRouteProvider.php b/core/modules/node/src/Entity/NodeHtmlRouteProvider.php
new file mode 100644
index 0000000..72b7a74
--- /dev/null
+++ b/core/modules/node/src/Entity/NodeHtmlRouteProvider.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Entity\NodeHtmlRouteProvider.
+ */
+
+namespace Drupal\node\Entity;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider;
+use Drupal\Core\Entity\Routing\EntityRouteProviderInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Provides HTML routes for the node entity type.
+ */
+class NodeHtmlRouteProvider extends DefaultHtmlRouteProvider {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoutes(EntityTypeInterface $entity_type) {
+    $collection = parent::getRoutes($entity_type);
+
+    $collection->get('entity.node.canonical')
+      ->setDefault('_entity_view', NULL)
+      ->setDefault('_controller', '\Drupal\node\Controller\NodeViewController::view');
+
+    $collection->get('entity.node.delete_form')
+      ->setOption('_node_operation_route', TRUE);
+
+    $collection->get('entity.node.edit_form')
+      ->setOption('_node_operation_route', TRUE);
+
+    return $collection;
+  }
+
+}
diff --git a/core/modules/node/src/Entity/NodeRouteProvider.php b/core/modules/node/src/Entity/NodeRouteProvider.php
deleted file mode 100644
index fa1c02d..0000000
--- a/core/modules/node/src/Entity/NodeRouteProvider.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\node\Entity\NodeRouteProvider.
- */
-
-namespace Drupal\node\Entity;
-
-use Drupal\Core\Entity\EntityTypeInterface;
-use Drupal\Core\Entity\Routing\EntityRouteProviderInterface;
-use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
-
-/**
- * Provides routes for nodes.
- */
-class NodeRouteProvider implements EntityRouteProviderInterface {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getRoutes( EntityTypeInterface $entity_type) {
-    $route_collection = new RouteCollection();
-    $route = (new Route('/node/{node}'))
-      ->addDefaults([
-        '_controller' => '\Drupal\node\Controller\NodeViewController::view',
-        '_title_callback' => '\Drupal\node\Controller\NodeViewController::title',
-      ])
-      ->setRequirement('_entity_access', 'node.view');
-    $route_collection->add('entity.node.canonical', $route);
-
-    $route = (new Route('/node/{node}/delete'))
-      ->addDefaults([
-        '_entity_form' => 'node.delete',
-        '_title' => 'Delete',
-      ])
-      ->setRequirement('_entity_access', 'node.delete')
-      ->setOption('_node_operation_route', TRUE);
-    $route_collection->add('entity.node.delete_form', $route);
-
-    $route = (new Route('/node/{node}/edit'))
-      ->setDefault('_entity_form', 'node.edit')
-      ->setRequirement('_entity_access', 'node.update')
-      ->setOption('_node_operation_route', TRUE);
-    $route_collection->add('entity.node.edit_form', $route);
-
-    return $route_collection;
-  }
-
-}
diff --git a/core/modules/rest/src/Routing/ResourceRoutes.php b/core/modules/rest/src/Routing/ResourceRoutes.php
index db439dc..ba21a81 100644
--- a/core/modules/rest/src/Routing/ResourceRoutes.php
+++ b/core/modules/rest/src/Routing/ResourceRoutes.php
@@ -59,6 +59,16 @@ public function __construct(ResourcePluginManager $manager, ConfigFactoryInterfa
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events = parent::getSubscribedEvents();
+
+    return $events;
+  }
+
+
+  /**
    * Alters existing routes for a specific collection.
    *
    * @param \Symfony\Component\Routing\RouteCollection $collection
@@ -71,6 +81,7 @@ protected function alterRoutes(RouteCollection $collection) {
 
     // Iterate over all enabled resource plugins.
     foreach ($enabled_resources as $id => $enabled_methods) {
+      /** @var \Drupal\rest\Plugin\ResourceInterface $plugin */
       $plugin = $this->manager->getInstance(array('id' => $id));
 
       foreach ($plugin->routes() as $name => $route) {
diff --git a/core/modules/rest/src/Tests/AuthTest.php b/core/modules/rest/src/Tests/AuthTest.php
index c8a8e3b..6f8c3c5 100644
--- a/core/modules/rest/src/Tests/AuthTest.php
+++ b/core/modules/rest/src/Tests/AuthTest.php
@@ -91,6 +91,7 @@ protected function basicAuthGet(Url $url, $username, $password) {
         CURLOPT_URL => $url->setAbsolute()->toString(),
         CURLOPT_NOBODY => FALSE,
         CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
+        CURLOPT_HTTPHEADER => array('Accept: ' . $this->defaultMimeType),
         CURLOPT_USERPWD => $username . ':' . $password,
       )
     );
diff --git a/core/modules/rest/src/Tests/ResourceTest.php b/core/modules/rest/src/Tests/ResourceTest.php
index be02617..80ffd35 100644
--- a/core/modules/rest/src/Tests/ResourceTest.php
+++ b/core/modules/rest/src/Tests/ResourceTest.php
@@ -39,6 +39,10 @@ protected function setUp() {
    * Tests that a resource without formats cannot be enabled.
    */
   public function testFormats() {
+    $account = $this->drupalCreateUser(['view test entity']);
+    $account->save();
+    $this->drupalLogin($account);
+
     $settings = array(
       'entity:entity_test' => array(
         'GET' => array(
diff --git a/core/modules/shortcut/shortcut.routing.yml b/core/modules/shortcut/shortcut.routing.yml
index d6c0e1a..df295a7 100644
--- a/core/modules/shortcut/shortcut.routing.yml
+++ b/core/modules/shortcut/shortcut.routing.yml
@@ -54,22 +54,6 @@ shortcut.link_add:
   requirements:
     _entity_create_access: 'shortcut:{shortcut_set}'
 
-entity.shortcut.canonical:
-  path: '/admin/config/user-interface/shortcut/link/{shortcut}'
-  defaults:
-    _entity_form: 'shortcut.default'
-    _title: 'Edit'
-  requirements:
-    _entity_access: 'shortcut.update'
-
-entity.shortcut.edit_form:
-  path: '/admin/config/user-interface/shortcut/link/{shortcut}'
-  defaults:
-    _entity_form: 'shortcut.default'
-    _title: 'Edit'
-  requirements:
-    _entity_access: 'shortcut.update'
-
 entity.shortcut.link_delete_inline:
   path: '/admin/config/user-interface/shortcut/link/{shortcut}/delete-inline'
   defaults:
@@ -78,14 +62,6 @@ entity.shortcut.link_delete_inline:
     _entity_access: 'shortcut.delete'
     _csrf_token: 'TRUE'
 
-entity.shortcut.delete_form:
-  path: '/admin/config/user-interface/shortcut/link/{shortcut}/delete'
-  defaults:
-    _entity_form: 'shortcut.delete'
-    _title: 'Delete'
-  requirements:
-    _entity_access: 'shortcut.delete'
-
 shortcut.set_switch:
   path: '/user/{user}/shortcuts'
   defaults:
diff --git a/core/modules/shortcut/src/Entity/Shortcut.php b/core/modules/shortcut/src/Entity/Shortcut.php
index 91a7f0b..79fdbf4 100644
--- a/core/modules/shortcut/src/Entity/Shortcut.php
+++ b/core/modules/shortcut/src/Entity/Shortcut.php
@@ -30,7 +30,10 @@
  *       "edit" = "Drupal\shortcut\ShortcutForm",
  *       "delete" = "Drupal\shortcut\Form\ShortcutDeleteForm"
  *     },
- *     "translation" = "Drupal\content_translation\ContentTranslationHandler"
+ *     "translation" = "Drupal\content_translation\ContentTranslationHandler",
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ *     },
  *   },
  *   base_table = "shortcut",
  *   data_table = "shortcut_field_data",
diff --git a/core/modules/system/src/Tests/Entity/EntityFormTest.php b/core/modules/system/src/Tests/Entity/EntityFormTest.php
index ce12dfe..2724125 100644
--- a/core/modules/system/src/Tests/Entity/EntityFormTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityFormTest.php
@@ -25,7 +25,7 @@ class EntityFormTest extends WebTestBase {
 
   protected function setUp() {
     parent::setUp();
-    $web_user = $this->drupalCreateUser(array('administer entity_test content'));
+    $web_user = $this->drupalCreateUser(array('administer entity_test content', 'view test entity'));
     $this->drupalLogin($web_user);
   }
 
diff --git a/core/modules/system/src/Tests/Entity/EntityRevisionsTest.php b/core/modules/system/src/Tests/Entity/EntityRevisionsTest.php
index d484c8b..e880928 100644
--- a/core/modules/system/src/Tests/Entity/EntityRevisionsTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityRevisionsTest.php
@@ -30,6 +30,7 @@ protected function setUp() {
     // Create and login user.
     $this->web_user = $this->drupalCreateUser(array(
       'administer entity_test content',
+      'view test entity',
     ));
     $this->drupalLogin($this->web_user);
   }
diff --git a/core/modules/system/src/Tests/Entity/EntityViewControllerTest.php b/core/modules/system/src/Tests/Entity/EntityViewControllerTest.php
index aa2d800..9dc73bc 100644
--- a/core/modules/system/src/Tests/Entity/EntityViewControllerTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityViewControllerTest.php
@@ -41,6 +41,9 @@ protected function setUp() {
       $this->entities[] = $entity_test;
     }
 
+    $account = $this->drupalCreateUser(['view test entity']);
+    $account->save();
+    $this->drupalLogin($account);
   }
 
   /**
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.routing.yml b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml
index 78551c6..5a7a5c9 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.routing.yml
+++ b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml
@@ -1,11 +1,3 @@
-entity.entity_test.canonical:
-  path: '/entity_test/{entity_test}'
-  defaults:
-    _entity_view: 'entity_test.full'
-    _title: 'Test full view mode'
-  requirements:
-    _access: 'TRUE'
-
 entity.entity_test.render_options:
   path: '/entity_test_converter/{foo}'
   options:
diff --git a/core/modules/system/tests/modules/entity_test/src/Controller/EntityTestController.php b/core/modules/system/tests/modules/entity_test/src/Controller/EntityTestController.php
index bf5fa29..d3ccfc5 100644
--- a/core/modules/system/tests/modules/entity_test/src/Controller/EntityTestController.php
+++ b/core/modules/system/tests/modules/entity_test/src/Controller/EntityTestController.php
@@ -63,26 +63,6 @@ public function testAdd($entity_type_id) {
   }
 
   /**
-   * Displays the 'Edit existing entity_test' form.
-   *
-   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
-   *   The route match object to get entity type from.
-   * @param string $entity_type_id
-   *   The entity type ID.
-   *
-   * @return array
-   *   The processed form for the edited entity.
-   *
-   * @see \Drupal\entity_test\Routing\EntityTestRoutes::routes()
-   */
-  public function testEdit(RouteMatchInterface $route_match, $entity_type_id) {
-    $entity = $route_match->getParameter($entity_type_id);
-    $form = $this->entityFormBuilder()->getForm($entity);
-    $form['#title'] = $entity->label();
-    return $form;
-  }
-
-  /**
    * Returns an empty page.
    *
    * @see \Drupal\entity_test\Routing\EntityTestRoutes::routes()
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php
index 0f444a1..f472605 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php
@@ -28,10 +28,14 @@
  *       "default" = "Drupal\entity_test\EntityTestForm",
  *       "delete" = "Drupal\entity_test\EntityTestDeleteForm"
  *     },
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ *     },
  *     "translation" = "Drupal\content_translation\ContentTranslationHandler",
  *     "views_data" = "Drupal\views\EntityViewsData"
  *   },
  *   base_table = "entity_test",
+ *   admin_permission = "administer entity_test content",
  *   persistent_cache = FALSE,
  *   entity_keys = {
  *     "id" = "id",
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php
index 210eb53..ca32b8c 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php
@@ -21,15 +21,24 @@
  *     "form" = {
  *       "default" = "Drupal\entity_test\EntityTestForm"
  *     },
- *     "translation" = "Drupal\content_translation\ContentTranslationHandler"
+ *     "translation" = "Drupal\content_translation\ContentTranslationHandler",
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ *     },
  *   },
  *   base_table = "entity_test",
+ *   admin_permission = "administer entity_test content",
  *   entity_keys = {
  *     "id" = "id",
  *     "uuid" = "uuid",
  *     "bundle" = "type"
  *   },
  *   links = {
+ *     "canonical" = "/entity_test_base_field_display/{entity_test_base_field_display}",
+ *     "edit-form" = "/entity_test_base_field_display/manage/{entity_test_base_field_display}",
+ *     "delete-form" = "/entity_test/delete/entity_test_base_field_display/{entity_test_base_field_display}",
+ *   },
+ *   links = {
  *     "edit-form" = "/entity_test_base_field_display/manage/{entity_test_base_field_display}",
  *   },
  *   field_ui_base_route = "entity.entity_test_base_field_display.admin_form",
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php
index 334ff94..c965452 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php
@@ -25,10 +25,14 @@
  *       "delete" = "Drupal\entity_test\EntityTestDeleteForm"
  *     },
  *     "translation" = "Drupal\content_translation\ContentTranslationHandler",
- *     "views_data" = "Drupal\views\EntityViewsData"
+ *     "views_data" = "Drupal\views\EntityViewsData",
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ *     },
  *   },
  *   base_table = "entity_test_mul",
  *   data_table = "entity_test_mul_property_data",
+ *   admin_permission = "administer entity_test content",
  *   translatable = TRUE,
  *   entity_keys = {
  *     "id" = "id",
@@ -38,7 +42,7 @@
  *     "langcode" = "langcode",
  *   },
  *   links = {
- *     "canonical" = "/entity_test_mul/manage/{entity_test_mul}",
+ *     "canonical" = "/entity_test_mul/{entity_test_mul}",
  *     "edit-form" = "/entity_test_mul/manage/{entity_test_mul}",
  *     "delete-form" = "/entity_test/delete/entity_test_mul/{entity_test_mul}",
  *   },
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulDefaultValue.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulDefaultValue.php
index 48b13e2..6e492c4 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulDefaultValue.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulDefaultValue.php
@@ -37,7 +37,7 @@
  *     "langcode" = "langcode"
  *   },
  *   links = {
- *     "canonical" = "/entity_test_mul_default_value/manage/{entity_test_mul_default_value}",
+ *     "canonical" = "/entity_test_mul_default_value/{entity_test_mul_default_value}",
  *     "edit-form" = "/entity_test_mul_default_value/manage/{entity_test_mul_default_value}",
  *     "delete-form" = "/entity_test/delete/entity_test_mul_default_value/{entity_test_mul_default_value}",
  *   },
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php
index 0b5585b..39a7b99 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php
@@ -23,10 +23,14 @@
  *       "delete" = "Drupal\entity_test\EntityTestDeleteForm"
  *     },
  *     "translation" = "Drupal\content_translation\ContentTranslationHandler",
- *     "views_data" = "Drupal\views\EntityViewsData"
+ *     "views_data" = "Drupal\views\EntityViewsData",
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ *     },
  *   },
  *   base_table = "entity_test_mul_langcode_key",
  *   data_table = "entity_test_mul_langcode_key_field_data",
+ *   admin_permission = "administer entity_test content",
  *   translatable = TRUE,
  *   entity_keys = {
  *     "id" = "id",
@@ -36,7 +40,7 @@
  *     "langcode" = "custom_langcode_key",
  *   },
  *   links = {
- *     "canonical" = "/entity_test_mul_langcode_key/manage/{entity_test_mul_langcode_key}",
+ *     "canonical" = "/entity_test_mul_langcode_key/{entity_test_mul_langcode_key}",
  *     "edit-form" = "/entity_test_mul_langcode_key/manage/{entity_test_mul_langcode_key}",
  *     "delete-form" = "/entity_test/delete/entity_test_mul_langcode_key/{entity_test_mul_langcode_key}",
  *   },
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
index 94b4bbe..67a26ca 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
@@ -25,12 +25,16 @@
  *       "delete" = "Drupal\entity_test\EntityTestDeleteForm"
  *     },
  *     "translation" = "Drupal\content_translation\ContentTranslationHandler",
- *     "views_data" = "Drupal\views\EntityViewsData"
+ *     "views_data" = "Drupal\views\EntityViewsData",
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ *     },
  *   },
  *   base_table = "entity_test_mulrev",
  *   data_table = "entity_test_mulrev_property_data",
  *   revision_table = "entity_test_mulrev_revision",
  *   revision_data_table = "entity_test_mulrev_property_revision",
+ *   admin_permission = "administer entity_test content",
  *   translatable = TRUE,
  *   entity_keys = {
  *     "id" = "id",
@@ -41,7 +45,7 @@
  *     "langcode" = "langcode",
  *   },
  *   links = {
- *     "canonical" = "/entity_test_mulrev/manage/{entity_test_mulrev}",
+ *     "canonical" = "/entity_test_mulrev/{entity_test_mulrev}",
  *     "delete-form" = "/entity_test/delete/entity_test_mulrev/{entity_test_mulrev}",
  *     "edit-form" = "/entity_test_mulrev/manage/{entity_test_mulrev}",
  *   }
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
index 3859a02..5638d2e 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
@@ -19,15 +19,20 @@
  *   label = @Translation("Test entity - revisions"),
  *   handlers = {
  *     "access" = "Drupal\entity_test\EntityTestAccessControlHandler",
+ *     "view_builder" = "Drupal\entity_test\EntityTestViewBuilder",
  *     "form" = {
  *       "default" = "Drupal\entity_test\EntityTestForm",
  *       "delete" = "Drupal\entity_test\EntityTestDeleteForm"
  *     },
  *     "translation" = "Drupal\content_translation\ContentTranslationHandler",
- *     "views_data" = "Drupal\views\EntityViewsData"
+ *     "views_data" = "Drupal\views\EntityViewsData",
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ *     },
  *   },
  *   base_table = "entity_test_rev",
  *   revision_table = "entity_test_rev_revision",
+ *   admin_permission = "administer entity_test content",
  *   entity_keys = {
  *     "id" = "id",
  *     "uuid" = "uuid",
@@ -37,7 +42,7 @@
  *     "langcode" = "langcode",
  *   },
  *   links = {
- *     "canonical" = "/entity_test_rev/manage/{entity_test_rev}",
+ *     "canonical" = "/entity_test_rev/{entity_test_rev}",
  *     "delete-form" = "/entity_test/delete/entity_test_rev/{entity_test_rev}",
  *     "edit-form" = "/entity_test_rev/manage/{entity_test_rev}"
  *   }
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestStringId.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestStringId.php
index 7e057d4..62a42e6 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestStringId.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestStringId.php
@@ -20,16 +20,20 @@
  *     "form" = {
  *       "default" = "Drupal\entity_test\EntityTestForm"
  *     },
- *     "translation" = "Drupal\content_translation\ContentTranslationHandler"
+ *     "translation" = "Drupal\content_translation\ContentTranslationHandler",
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ *     },
  *   },
  *   base_table = "entity_test_string",
+ *   admin_permission = "administer entity_test content",
  *   entity_keys = {
  *     "id" = "id",
  *     "uuid" = "uuid",
  *     "bundle" = "type"
  *   },
  *   links = {
- *     "canonical" = "/entity_test_string_id/manage/{entity_test_string_id}",
+ *     "canonical" = "/entity_test_string_id/{entity_test_string_id}",
  *     "edit-form" = "/entity_test_string_id/manage/{entity_test_string_id}",
  *   },
  *   field_ui_base_route = "entity.entity_test_string_id.admin_form",
diff --git a/core/modules/system/tests/modules/entity_test/src/Routing/EntityTestRoutes.php b/core/modules/system/tests/modules/entity_test/src/Routing/EntityTestRoutes.php
index 53c1978..fcaefca 100644
--- a/core/modules/system/tests/modules/entity_test/src/Routing/EntityTestRoutes.php
+++ b/core/modules/system/tests/modules/entity_test/src/Routing/EntityTestRoutes.php
@@ -33,30 +33,6 @@ public function routes() {
         array('_permission' => 'administer entity_test content')
       );
 
-      $routes["entity.$entity_type_id.canonical"] = new Route(
-        $entity_type_id . '/manage/{' . $entity_type_id . '}',
-        array('_controller' => '\Drupal\entity_test\Controller\EntityTestController::testEdit', 'entity_type_id' => $entity_type_id),
-        array('_permission' => 'administer entity_test content'),
-        array('parameters' => array(
-          $entity_type_id => array('type' => 'entity:' . $entity_type_id),
-        ))
-      );
-
-      $routes["entity.$entity_type_id.edit_form"] = new Route(
-        $entity_type_id . '/manage/{' . $entity_type_id . '}',
-        array('_controller' => '\Drupal\entity_test\Controller\EntityTestController::testEdit', 'entity_type_id' => $entity_type_id),
-        array('_permission' => 'administer entity_test content'),
-        array('parameters' => array(
-          $entity_type_id => array('type' => 'entity:' . $entity_type_id),
-        ))
-      );
-
-      $routes["entity.$entity_type_id.delete_form"] = new Route(
-        'entity_test/delete/' . $entity_type_id . '/{' . $entity_type_id . '}',
-        array('_entity_form' => $entity_type_id . '.delete'),
-        array('_permission' => 'administer entity_test content')
-      );
-
       $routes["entity.$entity_type_id.admin_form"] = new Route(
         "$entity_type_id/structure/{bundle}",
         array('_controller' => '\Drupal\entity_test\Controller\EntityTestController::testAdmin'),
diff --git a/core/modules/taxonomy/src/Controller/TaxonomyController.php b/core/modules/taxonomy/src/Controller/TaxonomyController.php
index e24eb6f..95c387e 100644
--- a/core/modules/taxonomy/src/Controller/TaxonomyController.php
+++ b/core/modules/taxonomy/src/Controller/TaxonomyController.php
@@ -47,19 +47,6 @@ public function addForm(VocabularyInterface $taxonomy_vocabulary) {
   /**
    * Route title callback.
    *
-   * @param \Drupal\taxonomy\VocabularyInterface $taxonomy_vocabulary
-   *   The taxonomy term.
-   *
-   * @return string
-   *   The term label.
-   */
-  public function vocabularyTitle(VocabularyInterface $taxonomy_vocabulary) {
-    return Xss::filter($taxonomy_vocabulary->label());
-  }
-
-  /**
-   * Route title callback.
-   *
    * @param \Drupal\taxonomy\TermInterface $taxonomy_term
    *   The taxonomy term.
    *
diff --git a/core/modules/taxonomy/src/Entity/Term.php b/core/modules/taxonomy/src/Entity/Term.php
index 043c90d..5b36e5c 100644
--- a/core/modules/taxonomy/src/Entity/Term.php
+++ b/core/modules/taxonomy/src/Entity/Term.php
@@ -30,7 +30,10 @@
  *       "default" = "Drupal\taxonomy\TermForm",
  *       "delete" = "Drupal\taxonomy\Form\TermDeleteForm"
  *     },
- *     "translation" = "Drupal\taxonomy\TermTranslationHandler"
+ *     "translation" = "Drupal\taxonomy\TermTranslationHandler",
+ *     "route_provider" = {
+ *       "html" = "Drupal\taxonomy\Entity\TermHtmlRouteProvider",
+ *     },
  *   },
  *   base_table = "taxonomy_term_data",
  *   data_table = "taxonomy_term_field_data",
diff --git a/core/modules/taxonomy/src/Entity/TermHtmlRouteProvider.php b/core/modules/taxonomy/src/Entity/TermHtmlRouteProvider.php
new file mode 100644
index 0000000..13c28c2
--- /dev/null
+++ b/core/modules/taxonomy/src/Entity/TermHtmlRouteProvider.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\taxonomy\Entity\TermHtmlRouteProvider.
+ */
+
+namespace Drupal\taxonomy\Entity;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider;
+
+/**
+ * Provides routes for the taxonomy_term entity.
+ */
+class TermHtmlRouteProvider extends DefaultHtmlRouteProvider {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoutes(EntityTypeInterface $entity_type) {
+    $collection = parent::getRoutes($entity_type);
+
+    $collection->get('entity.taxonomy_term.edit_form')
+      ->setOption('_admin_route', TRUE);
+
+    $collection->get('entity.taxonomy_term.delete_form')
+      ->setOption('_admin_route', TRUE);
+
+    return $collection;
+  }
+
+}
diff --git a/core/modules/taxonomy/src/Entity/Vocabulary.php b/core/modules/taxonomy/src/Entity/Vocabulary.php
index 4f21f8a..3d54420 100644
--- a/core/modules/taxonomy/src/Entity/Vocabulary.php
+++ b/core/modules/taxonomy/src/Entity/Vocabulary.php
@@ -25,7 +25,10 @@
  *       "default" = "Drupal\taxonomy\VocabularyForm",
  *       "reset" = "Drupal\taxonomy\Form\VocabularyResetForm",
  *       "delete" = "Drupal\taxonomy\Form\VocabularyDeleteForm"
- *     }
+ *     },
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ *     },
  *   },
  *   admin_permission = "administer taxonomy",
  *   config_prefix = "vocabulary",
diff --git a/core/modules/taxonomy/taxonomy.routing.yml b/core/modules/taxonomy/taxonomy.routing.yml
index af86c9e..cd542e5 100644
--- a/core/modules/taxonomy/taxonomy.routing.yml
+++ b/core/modules/taxonomy/taxonomy.routing.yml
@@ -14,26 +14,6 @@ entity.taxonomy_term.add_form:
   requirements:
     _entity_create_access: 'taxonomy_term:{taxonomy_vocabulary}'
 
-entity.taxonomy_term.edit_form:
-  path: '/taxonomy/term/{taxonomy_term}/edit'
-  defaults:
-    _entity_form: 'taxonomy_term.default'
-    _title: 'Edit term'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _entity_access: 'taxonomy_term.update'
-
-entity.taxonomy_term.delete_form:
-  path: '/taxonomy/term/{taxonomy_term}/delete'
-  defaults:
-    _entity_form: 'taxonomy_term.delete'
-    _title: 'Delete term'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _entity_access: 'taxonomy_term.delete'
-
 entity.taxonomy_vocabulary.add_form:
   path: '/admin/structure/taxonomy/add'
   defaults:
@@ -42,21 +22,6 @@ entity.taxonomy_vocabulary.add_form:
   requirements:
     _entity_create_access: 'taxonomy_vocabulary'
 
-entity.taxonomy_vocabulary.edit_form:
-  path: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}'
-  defaults:
-    _entity_form: 'taxonomy_vocabulary.default'
-    _title_callback: '\Drupal\taxonomy\Controller\TaxonomyController::vocabularyTitle'
-  requirements:
-    _entity_access: 'taxonomy_vocabulary.update'
-
-entity.taxonomy_vocabulary.delete_form:
-  path: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/delete'
-  defaults:
-    _entity_form: 'taxonomy_vocabulary.delete'
-    _title: 'Delete vocabulary'
-  requirements:
-    _entity_access: 'taxonomy_vocabulary.delete'
 
 entity.taxonomy_vocabulary.reset_form:
   path: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/reset'
@@ -84,15 +49,7 @@ entity.taxonomy_vocabulary.overview_form:
   path: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/overview'
   defaults:
     _form: 'Drupal\taxonomy\Form\OverviewTerms'
-    _title_callback: 'Drupal\taxonomy\Controller\TaxonomyController::vocabularyTitle'
+    _title_callback: 'Drupal\Core\Entity\Controller\EntityController::title'
   requirements:
     _entity_access: 'taxonomy_vocabulary.view'
 
-entity.taxonomy_term.canonical:
-  path: '/taxonomy/term/{taxonomy_term}'
-  defaults:
-    _entity_view: 'taxonomy_term.full'
-    _title: 'Taxonomy term'
-    _title_callback: '\Drupal\taxonomy\Controller\TaxonomyController::termTitle'
-  requirements:
-    _entity_access: 'taxonomy_term.view'
diff --git a/core/modules/user/src/Entity/Role.php b/core/modules/user/src/Entity/Role.php
index b3dcdb5..27f5473 100644
--- a/core/modules/user/src/Entity/Role.php
+++ b/core/modules/user/src/Entity/Role.php
@@ -25,7 +25,10 @@
  *     "form" = {
  *       "default" = "Drupal\user\RoleForm",
  *       "delete" = "Drupal\user\Form\UserRoleDelete"
- *     }
+ *     },
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ *     },
  *   },
  *   admin_permission = "administer permissions",
  *   config_prefix = "role",
diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php
index 81386d7..e97dc73 100644
--- a/core/modules/user/src/Entity/User.php
+++ b/core/modules/user/src/Entity/User.php
@@ -31,7 +31,7 @@
  *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
  *     "views_data" = "Drupal\user\UserViewsData",
  *     "route_provider" = {
- *       "html" = "Drupal\user\Entity\UserRouteProvider",
+ *       "html" = "Drupal\user\Entity\UserHtmlRouteProvider",
  *     },
  *     "form" = {
  *       "default" = "Drupal\user\ProfileForm",
diff --git a/core/modules/user/src/Entity/UserHtmlRouteProvider.php b/core/modules/user/src/Entity/UserHtmlRouteProvider.php
new file mode 100644
index 0000000..80e0559
--- /dev/null
+++ b/core/modules/user/src/Entity/UserHtmlRouteProvider.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\Entity\UserHtmlRouteProvider.
+ */
+
+namespace Drupal\user\Entity;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Provides HTML routes for the user entity type.
+ */
+class UserHtmlRouteProvider extends DefaultHtmlRouteProvider {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoutes(EntityTypeInterface $entity_type) {
+    $collection = parent::getRoutes($entity_type);
+
+    $collection->get('entity.user.canonical')->setDefault('_title_callback', 'Drupal\user\Controller\UserController::userTitle');
+
+    $collection->get('entity.user.edit_form')
+      ->setDefault('_title_callback', 'Drupal\user\Controller\UserController::userTitle')
+      ->setOption('_admin_route', TRUE);
+
+    $collection->remove('entity.user.delete');
+
+    $route = (new Route('/user/{user}/cancel'))
+      ->setDefaults([
+        '_title' => 'Cancel account',
+        '_entity_form' => 'user.cancel',
+      ])
+      ->setOption('_admin_route', TRUE)
+      ->setRequirement('_entity_access', 'user.delete');
+    $collection->add('entity.user.cancel_form', $route);
+
+    return $collection;
+  }
+
+}
diff --git a/core/modules/user/src/Entity/UserRouteProvider.php b/core/modules/user/src/Entity/UserRouteProvider.php
deleted file mode 100644
index f4cfd79..0000000
--- a/core/modules/user/src/Entity/UserRouteProvider.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\user\Entity\UserRouteProvider.
- */
-
-namespace Drupal\user\Entity;
-
-use Drupal\Core\Entity\EntityTypeInterface;
-use Drupal\Core\Entity\Routing\EntityRouteProviderInterface;
-use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
-
-/**
- * Provides routes for the user entity.
- */
-class UserRouteProvider implements EntityRouteProviderInterface {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getRoutes(EntityTypeInterface $entity_type) {
-    $route_collection = new RouteCollection();
-    $route = (new Route('/user/{user}'))
-      ->setDefaults([
-        '_entity_view' => 'user.full',
-        '_title_callback' => 'Drupal\user\Controller\UserController::userTitle',
-      ])
-      ->setRequirement('_entity_access', 'user.view');
-    $route_collection->add('entity.user.canonical', $route);
-
-    $route = (new Route('/user/{user}/edit'))
-      ->setDefaults([
-        '_entity_form' => 'user.default',
-        '_title_callback' => 'Drupal\user\Controller\UserController::userTitle',
-      ])
-      ->setOption('_admin_route', TRUE)
-      ->setRequirement('_entity_access', 'user.update');
-    $route_collection->add('entity.user.edit_form', $route);
-
-    $route = (new Route('/user/{user}/cancel'))
-      ->setDefaults([
-        '_title' => 'Cancel account',
-        '_entity_form' => 'user.cancel',
-      ])
-      ->setOption('_admin_route', TRUE)
-      ->setRequirement('_entity_access', 'user.delete');
-    $route_collection->add('entity.user.cancel_form', $route);
-
-    return $route_collection;
-  }
-
-}
diff --git a/core/modules/user/user.routing.yml b/core/modules/user/user.routing.yml
index 4f6a263..fc18865 100644
--- a/core/modules/user/user.routing.yml
+++ b/core/modules/user/user.routing.yml
@@ -99,22 +99,6 @@ user.role_add:
   requirements:
     _permission: 'administer permissions'
 
-entity.user_role.edit_form:
-  path: '/admin/people/roles/manage/{user_role}'
-  defaults:
-    _entity_form: user_role.default
-    _title: 'Edit role'
-  requirements:
-    _entity_access: user_role.update
-
-entity.user_role.delete_form:
-  path: '/admin/people/roles/manage/{user_role}/delete'
-  defaults:
-    _entity_form: user_role.delete
-    _title: 'Delete role'
-  requirements:
-    _entity_access: user_role.delete
-
 user.pass:
   path: '/user/password'
   defaults:
diff --git a/core/modules/views_ui/src/ViewHtmlRouteProvider.php b/core/modules/views_ui/src/ViewHtmlRouteProvider.php
new file mode 100644
index 0000000..61bdf83
--- /dev/null
+++ b/core/modules/views_ui/src/ViewHtmlRouteProvider.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\views_ui\ViewHtmlRouteProvider.
+ */
+
+namespace Drupal\views_ui;
+
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider;
+
+/**
+ * Provides HTML routes for the view entity type.
+ */
+class ViewHtmlRouteProvider extends DefaultHtmlRouteProvider {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoutes(EntityTypeInterface $entity_type) {
+    $collection = parent::getRoutes($entity_type);
+
+    $collection->get('entity.view.edit_form')
+      // Replace the _entity_form with a custom _controller.
+      ->setDefault('_entity_form', NULL)
+      ->setDefault('_controller', '\Drupal\views_ui\Controller\ViewsUIController::edit')
+      ->setOptions(['parameters' => ['view' => ['tempstore' => TRUE, 'type' => 'entity:view']]]);
+
+    return $collection;
+  }
+
+}
diff --git a/core/modules/views_ui/views_ui.module b/core/modules/views_ui/views_ui.module
index 5ac6790..901481c 100644
--- a/core/modules/views_ui/views_ui.module
+++ b/core/modules/views_ui/views_ui.module
@@ -53,6 +53,7 @@ function views_ui_entity_type_build(array &$entity_types) {
     ->setFormClass('delete', 'Drupal\views_ui\ViewDeleteForm')
     ->setFormClass('break_lock', 'Drupal\views_ui\Form\BreakLockForm')
     ->setListBuilderClass('Drupal\views_ui\ViewListBuilder')
+    ->setHandlerClass('route_provider', ['html' => 'Drupal\views_ui\ViewHtmlRouteProvider'])
     ->setLinkTemplate('edit-form', '/admin/structure/views/view/{view}')
     ->setLinkTemplate('edit-display-form', '/admin/structure/views/view/{view}/edit/{display_id}')
     ->setLinkTemplate('preview-form', '/admin/structure/views/view/{view}/preview/{display_id}')
diff --git a/core/modules/views_ui/views_ui.routing.yml b/core/modules/views_ui/views_ui.routing.yml
index 0b95812..66b8cfa 100644
--- a/core/modules/views_ui/views_ui.routing.yml
+++ b/core/modules/views_ui/views_ui.routing.yml
@@ -72,14 +72,6 @@ entity.view.duplicate_form:
   requirements:
     _entity_access: view.duplicate
 
-entity.view.delete_form:
-  path: '/admin/structure/views/view/{view}/delete'
-  defaults:
-    _entity_form: 'view.delete'
-    _title: 'Delete view'
-  requirements:
-    _entity_access: view.delete
-
 views_ui.autocomplete:
   path: '/admin/views/ajax/autocomplete/tag'
   defaults:
@@ -87,18 +79,6 @@ views_ui.autocomplete:
   requirements:
     _permission: 'administer views'
 
-entity.view.edit_form:
-  path: '/admin/structure/views/view/{view}'
-  options:
-    parameters:
-      view:
-        tempstore: TRUE
-        type: entity:view
-  defaults:
-    _controller: '\Drupal\views_ui\Controller\ViewsUIController::edit'
-  requirements:
-    _entity_access: view.update
-
 entity.view.edit_display_form:
   path: '/admin/structure/views/view/{view}/edit/{display_id}'
   options:
