diff --git a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
index 637d48d5f60941cab3203944d79e444d3538151b..cec3c49935e13134921424b4c2dc93e8e6ad1569 100644
--- a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
@@ -10,6 +10,7 @@
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
 use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
@@ -44,15 +45,25 @@ public function __construct(ContentNegotiation $negotiation) {
    */
   public function onView(GetResponseForControllerResultEvent $event) {
 
-    $request = $event->getRequest();
-
-    $method = 'on' . $this->negotiation->getContentType($request);
-
-    if (method_exists($this, $method)) {
-      $event->setResponse($this->$method($event));
+    // For a master request, we process the result and wrap it as needed.
+    // For a subrequest, all we want is the string value.  We assume that
+    // is just an HTML string from a controller, so wrap that into a response
+    // object.  The subrequest's response will get dissected and placed into
+    // the larger page as needed.
+    if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) {
+      $request = $event->getRequest();
+
+      $method = 'on' . $this->negotiation->getContentType($request);
+
+      if (method_exists($this, $method)) {
+        $event->setResponse($this->$method($event));
+      }
+      else {
+        $event->setResponse(new Response('Unsupported Media Type', 415));
+      }
     }
     else {
-      $event->setResponse(new Response('Unsupported Media Type', 415));
+      $event->setResponse(new Response($event->getControllerResult()));
     }
   }
 
diff --git a/core/lib/Drupal/Core/HtmlPageController.php b/core/lib/Drupal/Core/HtmlPageController.php
index bfc6d578b62fddb84a59ee9971f44a201d25e14a..b087b82c67d88309a0aa5a81ef473eb7d4a70cb8 100644
--- a/core/lib/Drupal/Core/HtmlPageController.php
+++ b/core/lib/Drupal/Core/HtmlPageController.php
@@ -22,11 +22,20 @@ public function setContainer(ContainerInterface $container = NULL) {
 
   public function content(Request $request, $_content) {
 
-    $content_controller = $this->getContentController($_content);
-
-    $page_callback_result = call_user_func_array($content_controller, array());
-
-    return new Response(drupal_render_page($page_callback_result));
+    // @todo When we have a Generator, we can replace the forward() call with
+    // a render() call, which would handle ESI and hInclude as well.  That will
+    // require an _internal route.  For examples, see:
+    // https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing/internal.xml
+    // https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/InternalController.php
+    $attributes = $request->attributes;
+    $controller = $attributes->get('_content');
+    $attributes->remove('system_path');
+    $attributes->remove('_content');
+    $response = $this->container->get('http_kernel')->forward($controller, $attributes->all(), $request->query->all());
+
+    $page_content = $response->getContent();
+
+    return new Response(drupal_render_page($page_content));
   }
 
   protected function getContentController($controller) {