diff --git a/core/core.services.yml b/core/core.services.yml index ca74a8c16909f2beef4de81908d65d52d636a33f..a7a2b3bdbce9a606ca54015f55de37c7febf5a64 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -486,6 +486,18 @@ services: http_kernel.basic: class: Symfony\Component\HttpKernel\HttpKernel arguments: ['@event_dispatcher', '@controller_resolver', '@request_stack'] + http_negotiation.format_negotiator: + class: Drupal\Core\ContentNegotiation + private: true + http_middleware.negotiation: + class: Drupal\Core\StackMiddleware\NegotiationMiddleware + arguments: ['@http_negotiation.format_negotiator'] + calls: + - [registerFormat, ['drupal_ajax', ['application/vnd.drupal-ajax']]] + - [registerFormat, ['drupal_dialog', ['application/vnd.drupal-dialog']]] + - [registerFormat, ['drupal_modal', ['application/vnd.drupal-modal']]] + tags: + - { name: http_middleware, priority: 400 } http_middleware.reverse_proxy: class: Drupal\Core\StackMiddleware\ReverseProxyMiddleware arguments: ['@settings'] @@ -493,7 +505,7 @@ services: - { name: http_middleware, priority: 300 } http_middleware.page_cache: class: Drupal\Core\StackMiddleware\PageCache - arguments: ['@cache.render', '@page_cache_request_policy', '@page_cache_response_policy', '@content_negotiation'] + arguments: ['@cache.render', '@page_cache_request_policy', '@page_cache_response_policy'] tags: - { name: http_middleware, priority: 200 } http_middleware.kernel_pre_handle: @@ -571,7 +583,7 @@ services: - { name: backend_overridable } router.route_preloader: class: Drupal\Core\Routing\RoutePreloader - arguments: ['@router.route_provider', '@state', '@content_negotiation'] + arguments: ['@router.route_provider', '@state'] tags: - { name: 'event_subscriber' } router.matcher.final_matcher: @@ -682,7 +694,6 @@ services: arguments: [16] accept_header_matcher: class: Drupal\Core\Routing\AcceptHeaderMatcher - arguments: ['@content_negotiation'] tags: - { name: route_filter } content_type_header_matcher: @@ -749,7 +760,6 @@ services: - { name: route_enhancer, priority: 20 } route_content_controller_subscriber: class: Drupal\Core\EventSubscriber\ContentControllerSubscriber - arguments: ['@content_negotiation'] tags: - { name: event_subscriber } route_special_attributes_subscriber: @@ -799,13 +809,11 @@ services: tags: - { name: event_subscriber } arguments: ['@router', '@router.request_context', NULL, '@request_stack'] - content_negotiation: - class: Drupal\Core\ContentNegotiation view_subscriber: class: Drupal\Core\EventSubscriber\ViewSubscriber + arguments: ['@title_resolver'] tags: - { name: event_subscriber } - arguments: ['@content_negotiation', '@title_resolver'] bare_html_page_renderer: class: Drupal\Core\Render\BareHtmlPageRenderer arguments: ['@renderer'] @@ -1003,10 +1011,6 @@ services: - { name: event_subscriber } calls: - [setContainer, ['@service_container']] - ajax.subscriber: - class: Drupal\Core\Ajax\AjaxSubscriber - tags: - - { name: event_subscriber } image.toolkit.manager: class: Drupal\Core\ImageToolkit\ImageToolkitManager arguments: ['@config.factory'] diff --git a/core/lib/Drupal/Core/Ajax/AjaxSubscriber.php b/core/lib/Drupal/Core/Ajax/AjaxSubscriber.php deleted file mode 100644 index f298d88e81e4f17ffe7ad0af9cda8d422a5954ae..0000000000000000000000000000000000000000 --- a/core/lib/Drupal/Core/Ajax/AjaxSubscriber.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\Core\Ajax\AjaxSubscriber. - */ - -namespace Drupal\Core\Ajax; - -use Symfony\Component\HttpKernel\KernelEvents; -use Symfony\Component\HttpKernel\Event\GetResponseEvent; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -/** - * Subscribes to the kernel request event to add the Ajax media type. - * - * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event - * The event to process. - */ -class AjaxSubscriber implements EventSubscriberInterface { - - /** - * Registers Ajax formats with the Request class. - */ - public function onKernelRequest(GetResponseEvent $event) { - $request = $event->getRequest(); - $request->setFormat('drupal_ajax', 'application/vnd.drupal-ajax'); - $request->setFormat('drupal_dialog', 'application/vnd.drupal-dialog'); - $request->setFormat('drupal_modal', 'application/vnd.drupal-modal'); - } - - /** - * Registers the methods in this class that should be listeners. - * - * @return array - * An array of event listener definitions. - */ - static function getSubscribedEvents(){ - $events[KernelEvents::REQUEST][] = array('onKernelRequest', 50); - return $events; - } - -} \ No newline at end of file diff --git a/core/lib/Drupal/Core/EventSubscriber/ContentControllerSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ContentControllerSubscriber.php index 32f9565e1bf67849a34239a0d2d254787d8b3e4b..16e96139343ea3814ce135694a2b84d0a354b444 100644 --- a/core/lib/Drupal/Core/EventSubscriber/ContentControllerSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/ContentControllerSubscriber.php @@ -7,7 +7,6 @@ namespace Drupal\Core\EventSubscriber; -use Drupal\Core\ContentNegotiation; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; @@ -20,39 +19,6 @@ */ class ContentControllerSubscriber implements EventSubscriberInterface { - /** - * Content negotiation library. - * - * @var \Drupal\Core\ContentNegotiation - */ - protected $negotiation; - - /** - * Constructs a new ContentControllerSubscriber object. - * - * @param \Drupal\Core\ContentNegotiation $negotiation - * The Content Negotiation service. - */ - public function __construct(ContentNegotiation $negotiation) { - $this->negotiation = $negotiation; - } - - /** - * Sets the derived request format on the request. - * - * @todo Remove when https://www.drupal.org/node/2331919 lands. - * - * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event - * The event to process. - */ - public function onRequestDeriveFormat(GetResponseEvent $event) { - $request = $event->getRequest(); - - if (!$request->attributes->get('_format')) { - $request->setRequestFormat($this->negotiation->getContentType($request)); - } - } - /** * Sets the _controller on a request when a _form is defined. * @@ -74,7 +40,6 @@ public function onRequestDeriveFormWrapper(GetResponseEvent $event) { * An array of event listener definitions. */ static function getSubscribedEvents() { - $events[KernelEvents::REQUEST][] = array('onRequestDeriveFormat', 31); $events[KernelEvents::REQUEST][] = array('onRequestDeriveFormWrapper', 29); return $events; diff --git a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php index 03f26918dab2d1106a21fa18aa0a638be3f27903..b3d22025c3d2eec828f03234c8a1106f6453533e 100644 --- a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php @@ -10,7 +10,6 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\String; use Drupal\Core\Config\ConfigFactoryInterface; -use Drupal\Core\ContentNegotiation; use Drupal\Core\Render\BareHtmlPageRendererInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\Utility\Error; @@ -206,8 +205,7 @@ protected function getFormat(Request $request) { // to this code. We therefore use this style for now on the expectation // that it will get replaced with better code later. This approach makes // that change easier when we get to it. - $conneg = new ContentNegotiation(); - $format = $conneg->getContentType($request); + $format = \Drupal::service('http_negotiation.format_negotiator')->getContentType($request); // These are all JSON errors for our purposes. Any special handling for // them can/should happen in earlier listeners if desired. diff --git a/core/lib/Drupal/Core/EventSubscriber/HttpExceptionSubscriberBase.php b/core/lib/Drupal/Core/EventSubscriber/HttpExceptionSubscriberBase.php index c9d9fa99145bcb3381308fe8a8de540c7555d97a..a14d327b16c4adf80361efbdec99085e1abc0444 100644 --- a/core/lib/Drupal/Core/EventSubscriber/HttpExceptionSubscriberBase.php +++ b/core/lib/Drupal/Core/EventSubscriber/HttpExceptionSubscriberBase.php @@ -7,7 +7,6 @@ namespace Drupal\Core\EventSubscriber; -use Drupal\Core\ContentNegotiation; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; @@ -91,16 +90,7 @@ public function onException(GetResponseForExceptionEvent $event) { $handled_formats = $this->getHandledFormats(); - // @todo Injecting this service would force all implementing classes to also - // handle its injection. However, we are trying to switch to a more robust - // content negotiation library in https://www.drupal.org/node/1505080 that - // will make $request->getRequestFormat() reliable as a better alternative - // to this code. We therefore use this style for now on the expectation - // that it will get replaced with better code later. That change will NOT - // be an API change for any implementing classes. (Whereas if we injected - // this class it would be an API change. That's why we're not doing it.) - $conneg = new ContentNegotiation(); - $format = $conneg->getContentType($event->getRequest()); + $format = $event->getRequest()->getRequestFormat(); if ($exception instanceof HttpExceptionInterface && (empty($handled_formats) || in_array($format, $handled_formats))) { $method = 'on' . $exception->getStatusCode(); diff --git a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php index 0383a3779b3e3246969aec1ae781c6a6225ca263..3485f1928be70d6c6ead25d9cfff55d351ffb4c6 100644 --- a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php @@ -16,8 +16,6 @@ use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Drupal\Core\ContentNegotiation; - /** * Main subscriber for VIEW HTTP responses. * @@ -27,13 +25,6 @@ */ class ViewSubscriber implements EventSubscriberInterface { - /** - * The content negotiation. - * - * @var \Drupal\Core\ContentNegotiation - */ - protected $negotiation; - /** * The title resolver. * @@ -41,17 +32,13 @@ class ViewSubscriber implements EventSubscriberInterface { */ protected $titleResolver; - /** * Constructs a new ViewSubscriber. * - * @param \Drupal\Core\ContentNegotiation $negotiation - * The content negotiation. * @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver * The title resolver. */ - public function __construct(ContentNegotiation $negotiation, TitleResolverInterface $title_resolver) { - $this->negotiation = $negotiation; + public function __construct(TitleResolverInterface $title_resolver) { $this->titleResolver = $title_resolver; } @@ -64,14 +51,14 @@ public function __construct(ContentNegotiation $negotiation, TitleResolverInterf * from an JSON-type response is a JSON string, so just wrap it into a * Response object. * - * @param Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event + * @param \Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event * The Event to process. */ public function onView(GetResponseForControllerResultEvent $event) { $request = $event->getRequest(); if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) { - $method = 'on' . $this->negotiation->getContentType($request); + $method = 'on' . $request->getRequestFormat(); if (method_exists($this, $method)) { $event->setResponse($this->$method($event)); diff --git a/core/lib/Drupal/Core/Routing/AcceptHeaderMatcher.php b/core/lib/Drupal/Core/Routing/AcceptHeaderMatcher.php index 11760dd06844e8533ed1888be36f70ad5c5cd7a8..5c0618ce3d08155b628b5a8694d0133f81fc8e60 100644 --- a/core/lib/Drupal/Core/Routing/AcceptHeaderMatcher.php +++ b/core/lib/Drupal/Core/Routing/AcceptHeaderMatcher.php @@ -8,7 +8,6 @@ namespace Drupal\Core\Routing; use Drupal\Component\Utility\String; -use Drupal\Core\ContentNegotiation; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException; use Symfony\Component\Routing\Route; @@ -19,23 +18,6 @@ */ class AcceptHeaderMatcher implements RouteFilterInterface { - /** - * The content negotiation library. - * - * @var \Drupal\Core\ContentNegotiation - */ - protected $contentNegotiation; - - /** - * Constructs a new AcceptHeaderMatcher. - * - * @param \Drupal\Core\ContentNegotiation $content_negotiation - * The content negotiation library. - */ - public function __construct(ContentNegotiation $content_negotiation) { - $this->contentNegotiation = $content_negotiation; - } - /** * {@inheritdoc} */ @@ -44,7 +26,7 @@ public function filter(RouteCollection $collection, Request $request) { // @todo replace by proper content negotiation library. $acceptable_mime_types = $request->getAcceptableContentTypes(); $acceptable_formats = array_filter(array_map(array($request, 'getFormat'), $acceptable_mime_types)); - $primary_format = $this->contentNegotiation->getContentType($request); + $primary_format = $request->getRequestFormat(); foreach ($collection as $name => $route) { // _format could be a |-delimited list of supported formats. diff --git a/core/lib/Drupal/Core/Routing/RoutePreloader.php b/core/lib/Drupal/Core/Routing/RoutePreloader.php index 7952d177e6e81fa0d9d29a08e60c2999d6f1bfc9..1f223bfd48608753b32dbc716e699b74322008cc 100644 --- a/core/lib/Drupal/Core/Routing/RoutePreloader.php +++ b/core/lib/Drupal/Core/Routing/RoutePreloader.php @@ -7,7 +7,6 @@ namespace Drupal\Core\Routing; -use Drupal\Core\ContentNegotiation; use Drupal\Core\State\StateInterface; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -37,13 +36,6 @@ class RoutePreloader implements EventSubscriberInterface { */ protected $state; - /** - * The content negotiation. - * - * @var \Drupal\Core\ContentNegotiation - */ - protected $negotiation; - /** * Contains the non-admin routes while rebuilding the routes. * @@ -58,13 +50,10 @@ class RoutePreloader implements EventSubscriberInterface { * The route provider. * @param \Drupal\Core\State\StateInterface $state * The state key value store. - * @param \Drupal\Core\ContentNegotiation $negotiation - * The content negotiation. */ - public function __construct(RouteProviderInterface $route_provider, StateInterface $state, ContentNegotiation $negotiation) { + public function __construct(RouteProviderInterface $route_provider, StateInterface $state) { $this->routeProvider = $route_provider; $this->state = $state; - $this->negotiation = $negotiation; } /** @@ -75,7 +64,7 @@ public function __construct(RouteProviderInterface $route_provider, StateInterfa */ public function onRequest(KernelEvent $event) { // Just preload on normal HTML pages, as they will display menu links. - if ($this->negotiation->getContentType($event->getRequest()) == 'html') { + if ($event->getRequest()->getRequestFormat() == 'html') { $this->loadNonAdminRoutes(); } } diff --git a/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php b/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php new file mode 100644 index 0000000000000000000000000000000000000000..295857d626c5d68a3d8c1ce7350b0bc86a02e889 --- /dev/null +++ b/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php @@ -0,0 +1,83 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\StackMiddleware\NegotationMiddleware. + */ + +namespace Drupal\Core\StackMiddleware; + +use Drupal\Core\ContentNegotiation; +use Drupal\Core\ContentNegotiationInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * Provides a middleware to determine the content type upon the accept header. + * + * @todo This is a temporary solution, remove this in https://www.drupal.org/node/2364011 + */ +class NegotiationMiddleware implements HttpKernelInterface { + + /** + * The wrapped HTTP kernel. + * + * @var \Symfony\Component\HttpKernel\HttpKernelInterface + */ + protected $app; + + /** + * The content negotiator. + * + * @var \Drupal\Core\ContentNegotiation + */ + protected $negotiator; + + /** + * Contains a hashmap of format as key and mimetype as value. + * + * @var array + */ + protected $formats; + + /** + * Constructs a new NegotiationMiddleware. + * + * @param \Symfony\Component\HttpKernel\HttpKernelInterface $app + * The wrapper HTTP kernel + * @param \Drupal\Core\ContentNegotiation $negotiator + * The content negotiator. + */ + public function __construct(HttpKernelInterface $app, ContentNegotiation $negotiator) { + $this->app = $app; + $this->negotiator = $negotiator; + } + + /** + * {@inheritdoc} + */ + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) { + foreach ($this->formats as $format => $mime_type) { + $request->setFormat($format, $mime_type); + } + + $request->setRequestFormat($this->negotiator->getContentType($request)); + return $this->app->handle($request, $type, $catch); + } + + /** + * Registers a format for a given MIME type. + * + * @param string $format + * The format. + * @param string $mime_type + * The MIME type. + * + * @return $this + */ + public function registerFormat($format, $mime_type) { + $this->formats[$format] = $mime_type; + return $this; + } + +} diff --git a/core/lib/Drupal/Core/StackMiddleware/PageCache.php b/core/lib/Drupal/Core/StackMiddleware/PageCache.php index b381018f49d751d89d54020933a2a84488e588cd..d4ec291494e640ab79ae9bacb57ea207d8e8a063 100644 --- a/core/lib/Drupal/Core/StackMiddleware/PageCache.php +++ b/core/lib/Drupal/Core/StackMiddleware/PageCache.php @@ -9,7 +9,6 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheBackendInterface; -use Drupal\Core\ContentNegotiation; use Drupal\Core\PageCache\RequestPolicyInterface; use Drupal\Core\PageCache\ResponsePolicyInterface; use Drupal\Core\Site\Settings; @@ -52,13 +51,6 @@ class PageCache implements HttpKernelInterface { */ protected $responsePolicy; - /** - * The content negotiation library. - * - * @var \Drupal\Core\ContentNegotiation - */ - protected $contentNegotiation; - /** * Constructs a ReverseProxyMiddleware object. * @@ -70,15 +62,12 @@ class PageCache implements HttpKernelInterface { * A policy rule determining the cacheability of a request. * @param \Drupal\Core\PageCache\ResponsePolicyInterface $response_policy * A policy rule determining the cacheability of the response. - * @param \Drupal\Core\ContentNegotiation $content_negotiation - * The content negotiation library. */ - public function __construct(HttpKernelInterface $http_kernel, CacheBackendInterface $cache, RequestPolicyInterface $request_policy, ResponsePolicyInterface $response_policy, ContentNegotiation $content_negotiation) { + public function __construct(HttpKernelInterface $http_kernel, CacheBackendInterface $cache, RequestPolicyInterface $request_policy, ResponsePolicyInterface $response_policy) { $this->httpKernel = $http_kernel; $this->cache = $cache; $this->requestPolicy = $request_policy; $this->responsePolicy = $response_policy; - $this->contentNegotiation = $content_negotiation; } /** @@ -332,7 +321,7 @@ protected function set(Request $request, Response $response, $expire, array $tag protected function getCacheId(Request $request) { $cid_parts = array( $request->getUri(), - $this->contentNegotiation->getContentType($request), + $request->getRequestFormat(), ); return implode(':', $cid_parts); } diff --git a/core/modules/hal/hal.services.yml b/core/modules/hal/hal.services.yml index 4e8984168f6d2fd4372ea35adfbe7c7624063ad7..7e40df0d5b8c56ccff4f14eb61f7ed312c5f641d 100644 --- a/core/modules/hal/hal.services.yml +++ b/core/modules/hal/hal.services.yml @@ -26,7 +26,3 @@ services: class: Drupal\hal\Encoder\JsonEncoder tags: - { name: encoder, priority: 10, format: hal_json } - hal.subscriber: - class: Drupal\hal\HalSubscriber - tags: - - { name: event_subscriber } diff --git a/core/modules/hal/src/HalServiceProvider.php b/core/modules/hal/src/HalServiceProvider.php new file mode 100644 index 0000000000000000000000000000000000000000..d8ac47f0f08d33976e044fb66a0a28ff7fc068c4 --- /dev/null +++ b/core/modules/hal/src/HalServiceProvider.php @@ -0,0 +1,28 @@ +<?php + +/** + * @file + * Contains \Drupal\hal\HalServiceProvider. + */ + +namespace Drupal\hal; + +use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\DependencyInjection\ServiceModifierInterface; + +/** + * Adds hal+json as known format. + */ +class HalServiceProvider implements ServiceModifierInterface { + + /** + * {@inheritdoc} + */ + public function alter(ContainerBuilder $container) { + if ($container->has('http_middleware.negotiation')) { + $container->getDefinition('http_middleware.negotiation')->addMethodCall('registerFormat', ['hal_json', ['application/hal+json']]); + } + } + +} + diff --git a/core/modules/hal/src/HalSubscriber.php b/core/modules/hal/src/HalSubscriber.php deleted file mode 100644 index 93a70bb6ba8d808085d4f6339297a359fc0f33d7..0000000000000000000000000000000000000000 --- a/core/modules/hal/src/HalSubscriber.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\hal\HalSubscriber. - */ - -namespace Drupal\hal; - -use Symfony\Component\HttpKernel\KernelEvents; -use Symfony\Component\HttpKernel\Event\GetResponseEvent; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -/** - * Subscribes to the kernel request event to add HAL media types. - */ -class HalSubscriber implements EventSubscriberInterface { - - /** - * Registers HAL formats with the Request class. - * - * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event - * The event to process. - */ - public function onKernelRequest(GetResponseEvent $event) { - $request = $event->getRequest(); - $request->setFormat('hal_json', 'application/hal+json'); - } - - /** - * Registers the methods in this class that should be listeners. - * - * @return array - * An array of event listener definitions. - */ - static function getSubscribedEvents() { - $events[KernelEvents::REQUEST][] = array('onKernelRequest', 40); - return $events; - } - -} diff --git a/core/modules/rest/src/Plugin/views/display/RestExport.php b/core/modules/rest/src/Plugin/views/display/RestExport.php index 076eb1c0c149ef2365fced052ab6304cb08431bd..240c9de795425a775034b13528f806f25701f69f 100644 --- a/core/modules/rest/src/Plugin/views/display/RestExport.php +++ b/core/modules/rest/src/Plugin/views/display/RestExport.php @@ -10,7 +10,6 @@ use Drupal\Component\Utility\String; use Drupal\Core\State\StateInterface; use Drupal\Core\Routing\RouteProviderInterface; -use Drupal\Core\ContentNegotiation; use Drupal\views\ViewExecutable; use Drupal\views\Plugin\views\display\PathPluginBase; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -72,13 +71,6 @@ class RestExport extends PathPluginBase { */ protected $mimeType; - /** - * The content negotiation library. - * - * @var \Drupal\Core\ContentNegotiation - */ - protected $contentNegotiation; - /** * Constructs a Drupal\rest\Plugin\ResourceBase object. * @@ -92,12 +84,9 @@ class RestExport extends PathPluginBase { * The route provider * @param \Drupal\Core\State\StateInterface $state * The state key value store. - * @param \Drupal\Core\ContentNegotiation $content_negotiation - * The content negotiation library. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, StateInterface $state, ContentNegotiation $content_negotiation) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, StateInterface $state) { parent::__construct($configuration, $plugin_id, $plugin_definition, $route_provider, $state); - $this->contentNegotiation = $content_negotiation; } /** @@ -109,8 +98,7 @@ public static function create(ContainerInterface $container, array $configuratio $plugin_id, $plugin_definition, $container->get('router.route_provider'), - $container->get('state'), - $container->get('content_negotiation') + $container->get('state') ); } @@ -120,7 +108,7 @@ public static function create(ContainerInterface $container, array $configuratio public function initDisplay(ViewExecutable $view, array &$display, array &$options = NULL) { parent::initDisplay($view, $display, $options); - $request_content_type = $this->contentNegotiation->getContentType($this->view->getRequest()); + $request_content_type = $this->view->getRequest()->getRequestFormat(); // Only use the requested content type if it's not 'html'. If it is then // default to 'json' to aid debugging. // @todo Remove the need for this when we have better content negotiation. diff --git a/core/modules/rest/tests/src/Unit/CollectRoutesTest.php b/core/modules/rest/tests/src/Unit/CollectRoutesTest.php index 7a91831a1cbcd843eda6eaa12b78b669d0864dbf..7a2cbae2d95aa1705bb83523fa1bd3e89eda8eb0 100644 --- a/core/modules/rest/tests/src/Unit/CollectRoutesTest.php +++ b/core/modules/rest/tests/src/Unit/CollectRoutesTest.php @@ -40,16 +40,10 @@ protected function setUp() { $container = new ContainerBuilder(); - $content_negotiation = $this->getMockBuilder('\Drupal\Core\ContentNegotiation') - ->disableOriginalConstructor() - ->getMock(); - $request = $this->getMockBuilder('\Symfony\Component\HttpFoundation\Request') ->disableOriginalConstructor() ->getMock(); - $container->set('content_negotiation', $content_negotiation); - $this->view = $this->getMock('\Drupal\views\Entity\View', array('initHandlers'), array( array('id' => 'test_view'), 'view', diff --git a/core/modules/system/src/Tests/Bootstrap/PageCacheTest.php b/core/modules/system/src/Tests/Bootstrap/PageCacheTest.php index 0f7bc6d2b5a5378fe44884cb6307213b8367e784..c389a2a483dd2c2348be416826fd8d408ac448bd 100644 --- a/core/modules/system/src/Tests/Bootstrap/PageCacheTest.php +++ b/core/modules/system/src/Tests/Bootstrap/PageCacheTest.php @@ -11,6 +11,7 @@ use Drupal\Core\Routing\RequestContext; use Drupal\simpletest\WebTestBase; use Drupal\Core\Cache\Cache; +use Drupal\user\Entity\Role; /** * Enables the page cache and tests it with various HTTP requests. @@ -98,6 +99,50 @@ function testAcceptHeaderRequests() { $this->drupalGet($accept_header_cache_uri, array(), $json_accept_header); $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Json response was cached.'); $this->assertRaw('{"content":"oh hai this is json"}', 'The correct Json response was returned.'); + + // Enable REST support for nodes and hal+json. + \Drupal::service('module_installer')->install(['node', 'rest', 'hal']); + $this->drupalCreateContentType(['type' => 'article']); + $node = $this->drupalCreateNode(['type' => 'article']); + $node_uri = 'node/' . $node->id(); + $hal_json_accept_header = ['Accept: application/hal+json']; + /** @var \Drupal\user\RoleInterface $role */ + $role = Role::load('anonymous'); + $role->grantPermission('restful get entity:node'); + $role->save(); + + $this->drupalGet($node_uri); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); + $this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8'); + $this->drupalGet($node_uri); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT'); + $this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8'); + + // Now request a HAL page, we expect that the first request is a cache miss + // and it serves HTML. + $this->drupalGet($node_uri, [], $hal_json_accept_header); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); + $this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json'); + $this->drupalGet($node_uri, [], $hal_json_accept_header); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT'); + $this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json'); + + // Clear the page cache. After that request a HAL request, followed by an + // ordinary HTML one. + \Drupal::cache('render')->deleteAll(); + $this->drupalGet($node_uri, [], $hal_json_accept_header); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); + $this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json'); + $this->drupalGet($node_uri, [], $hal_json_accept_header); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT'); + $this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json'); + + $this->drupalGet($node_uri); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); + $this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8'); + $this->drupalGet($node_uri); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT'); + $this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8'); } /** diff --git a/core/tests/Drupal/Tests/Core/Routing/AcceptHeaderMatcherTest.php b/core/tests/Drupal/Tests/Core/Routing/AcceptHeaderMatcherTest.php index 6f47816e6a907abe79e342556fff91be926c5ee1..d0c811cafc781b8c02d49656dd1d6bfc9f5184d0 100644 --- a/core/tests/Drupal/Tests/Core/Routing/AcceptHeaderMatcherTest.php +++ b/core/tests/Drupal/Tests/Core/Routing/AcceptHeaderMatcherTest.php @@ -7,7 +7,6 @@ namespace Drupal\Tests\Core\Routing; -use Drupal\Core\ContentNegotiation; use Drupal\Core\Routing\AcceptHeaderMatcher; use Drupal\Tests\Core\Routing\RoutingFixtures; use Drupal\Tests\UnitTestCase; @@ -41,7 +40,7 @@ protected function setUp() { parent::setUp(); $this->fixtures = new RoutingFixtures(); - $this->matcher = new AcceptHeaderMatcher(new ContentNegotiation()); + $this->matcher = new AcceptHeaderMatcher(); } /** @@ -50,14 +49,14 @@ protected function setUp() { * @see Drupal\Tests\Core\Routing\AcceptHeaderMatcherTest::testAcceptFiltering() */ public function acceptFilterProvider() { - return array( + return [ // Check that JSON routes get filtered and prioritized correctly. - array('application/json, text/xml;q=0.9', 'route_c', 'route_e'), + ['application/json, text/xml;q=0.9', 'json', 'route_c', 'route_e'], // Tests a JSON request with alternative JSON MIME type Accept header. - array('application/x-json, text/xml;q=0.9', 'route_c', 'route_e'), + ['application/x-json, text/xml;q=0.9', 'json', 'route_c', 'route_e'], // Tests a standard HTML request. - array('text/html, text/xml;q=0.9', 'route_e', 'route_c'), - ); + ['text/html, text/xml;q=0.9', 'html', 'route_e', 'route_c'], + ]; } /** @@ -65,6 +64,8 @@ public function acceptFilterProvider() { * * @param string $accept_header * The HTTP Accept header value of the request. + * @param string $format + * The request format. * @param string $included_route * The route name that should survive the filter and be ranked first. * @param string $excluded_route @@ -72,11 +73,12 @@ public function acceptFilterProvider() { * * @dataProvider acceptFilterProvider */ - public function testAcceptFiltering($accept_header, $included_route, $excluded_route) { + public function testAcceptFiltering($accept_header, $format, $included_route, $excluded_route) { $collection = $this->fixtures->sampleRouteCollection(); $request = Request::create('path/two', 'GET'); $request->headers->set('Accept', $accept_header); + $request->setRequestFormat($format); $routes = $this->matcher->filter($collection, $request); $this->assertEquals(count($routes), 4, 'The correct number of routes was found.'); $this->assertNotNull($routes->get($included_route), "Route $included_route was found when matching $accept_header."); @@ -103,6 +105,8 @@ public function testNoRouteFound() { $request = Request::create('path/two', 'GET'); $request->headers->set('Accept', 'application/json, text/xml;q=0.9'); + $request->setRequestFormat('json'); + $this->matcher->filter($routes, $request); $this->matcher->filter($routes, $request); $this->fail('No exception was thrown.'); } diff --git a/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php b/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php index 109a3f28e560904021256fb6e44242f71848946b..5692e6a1df71e57d1f8671ce1ebd7122bcfbbebf 100644 --- a/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php +++ b/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php @@ -34,13 +34,6 @@ class RoutePreloaderTest extends UnitTestCase { */ protected $state; - /** - * The mocked content negotiator. - * - * @var \Drupal\Core\ContentNegotiation|\PHPUnit_Framework_MockObject_MockObject - */ - protected $negotiation; - /** * The tested preloader. * @@ -54,10 +47,7 @@ class RoutePreloaderTest extends UnitTestCase { protected function setUp() { $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); $this->state = $this->getMock('\Drupal\Core\State\StateInterface'); - $this->negotiation = $this->getMockBuilder('\Drupal\Core\ContentNegotiation') - ->disableOriginalConstructor() - ->getMock(); - $this->preloader = new RoutePreloader($this->routeProvider, $this->state, $this->negotiation); + $this->preloader = new RoutePreloader($this->routeProvider, $this->state); } /** @@ -136,12 +126,10 @@ public function testOnRequestNonHtml() { ->disableOriginalConstructor() ->getMock(); $request = new Request(); + $request->setRequestFormat('non-html'); $event->expects($this->any()) ->method('getRequest') ->will($this->returnValue($request)); - $this->negotiation->expects($this->once()) - ->method('getContentType') - ->will($this->returnValue('non-html')); $this->routeProvider->expects($this->never()) ->method('getRoutesByNames'); @@ -159,12 +147,10 @@ public function testOnRequestOnHtml() { ->disableOriginalConstructor() ->getMock(); $request = new Request(); + $request->setRequestFormat('html'); $event->expects($this->any()) ->method('getRequest') ->will($this->returnValue($request)); - $this->negotiation->expects($this->once()) - ->method('getContentType') - ->will($this->returnValue('html')); $this->routeProvider->expects($this->once()) ->method('getRoutesByNames')