Commit 30ca0d1d authored by catch's avatar catch

Issue #2481453 by dawehner, pwolanin, rteijeiro, neclimdul, znerol: Implement...

Issue #2481453 by dawehner, pwolanin, rteijeiro, neclimdul, znerol: Implement query parameter based content negotiation as alternative to extensions
parent 5340b188
...@@ -560,10 +560,6 @@ services: ...@@ -560,10 +560,6 @@ services:
http_middleware.negotiation: http_middleware.negotiation:
class: Drupal\Core\StackMiddleware\NegotiationMiddleware class: Drupal\Core\StackMiddleware\NegotiationMiddleware
arguments: ['@http_negotiation.format_negotiator'] 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: tags:
- { name: http_middleware, priority: 400 } - { name: http_middleware, priority: 400 }
http_middleware.reverse_proxy: http_middleware.reverse_proxy:
...@@ -758,8 +754,8 @@ services: ...@@ -758,8 +754,8 @@ services:
password: password:
class: Drupal\Core\Password\PhpassHashedPassword class: Drupal\Core\Password\PhpassHashedPassword
arguments: [16] arguments: [16]
accept_header_matcher: request_format_route_filter:
class: Drupal\Core\Routing\AcceptHeaderMatcher class: Drupal\Core\Routing\RequestFormatRouteFilter
tags: tags:
- { name: route_filter } - { name: route_filter }
content_type_header_matcher: content_type_header_matcher:
......
...@@ -10,10 +10,7 @@ ...@@ -10,10 +10,7 @@
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
/** /**
* This class is a central library for content type negotiation. * Provides content negotation based upon query parameters.
*
* @todo Replace this class with a real content negotiation library based on
* mod_negotiation. Development of that is a work in progress.
*/ */
class ContentNegotiation { class ContentNegotiation {
...@@ -36,21 +33,8 @@ public function getContentType(Request $request) { ...@@ -36,21 +33,8 @@ public function getContentType(Request $request) {
return 'iframeupload'; return 'iframeupload';
} }
// Check all formats, if priority format is found return it. if ($request->query->has('_format')) {
$first_found_format = FALSE; return $request->query->get('_format');
foreach ($request->getAcceptableContentTypes() as $mime_type) {
$format = $request->getFormat($mime_type);
if ($format === 'html') {
return $format;
}
if (!is_null($format) && !$first_found_format) {
$first_found_format = $format;
}
}
// No HTML found, return first found.
if ($first_found_format) {
return $first_found_format;
} }
if ($request->isXmlHttpRequest()) { if ($request->isXmlHttpRequest()) {
......
...@@ -198,13 +198,7 @@ public function onException(GetResponseForExceptionEvent $event) { ...@@ -198,13 +198,7 @@ public function onException(GetResponseForExceptionEvent $event) {
* The format as which to treat the exception. * The format as which to treat the exception.
*/ */
protected function getFormat(Request $request) { protected function getFormat(Request $request) {
// @todo We are trying to switch to a more robust content negotiation $format = $request->query->get(MainContentViewSubscriber::WRAPPER_FORMAT, $request->getRequestFormat());
// 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. This approach makes
// that change easier when we get to it.
$format = \Drupal::service('http_negotiation.format_negotiator')->getContentType($request);
// These are all JSON errors for our purposes. Any special handling for // These are all JSON errors for our purposes. Any special handling for
// them can/should happen in earlier listeners if desired. // them can/should happen in earlier listeners if desired.
......
...@@ -86,11 +86,12 @@ public function onException(GetResponseForExceptionEvent $event) { ...@@ -86,11 +86,12 @@ public function onException(GetResponseForExceptionEvent $event) {
$exception = $event->getException(); $exception = $event->getException();
// Make the exception available for example when rendering a block. // Make the exception available for example when rendering a block.
$event->getRequest()->attributes->set('exception', $exception); $request = $event->getRequest();
$request->attributes->set('exception', $exception);
$handled_formats = $this->getHandledFormats(); $handled_formats = $this->getHandledFormats();
$format = $event->getRequest()->getRequestFormat(); $format = $request->query->get(MainContentViewSubscriber::WRAPPER_FORMAT, $request->getRequestFormat());
if ($exception instanceof HttpExceptionInterface && (empty($handled_formats) || in_array($format, $handled_formats))) { if ($exception instanceof HttpExceptionInterface && (empty($handled_formats) || in_array($format, $handled_formats))) {
$method = 'on' . $exception->getStatusCode(); $method = 'on' . $exception->getStatusCode();
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
use Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface as BaseRouteFilterInterface; use Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface as BaseRouteFilterInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait; use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
...@@ -95,7 +96,9 @@ public function filter(RouteCollection $collection, Request $request) { ...@@ -95,7 +96,9 @@ public function filter(RouteCollection $collection, Request $request) {
if (isset($filter_ids)) { if (isset($filter_ids)) {
foreach ($filter_ids as $filter_id) { foreach ($filter_ids as $filter_id) {
$collection = $this->container->get($filter_id)->filter($collection, $request); if ($filter = $this->container->get($filter_id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
$collection = $filter->filter($collection, $request);
}
} }
} }
return $collection; return $collection;
......
<?php
/**
* @file
* Contains \Drupal\Core\Routing\RequestFormatRouteFilter.
*/
namespace Drupal\Core\Routing;
use Drupal\Component\Utility\SafeMarkup;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* Provides a route filter, which filters by the request format.
*/
class RequestFormatRouteFilter implements RouteFilterInterface {
/**
* {@inheritdoc}
*/
public function applies(Route $route) {
return $route->hasRequirement('_format');
}
/**
* {@inheritdoc}
*/
public function filter(RouteCollection $collection, Request $request) {
$format = $request->getRequestFormat('html');
/** @var \Symfony\Component\Routing\Route $route */
foreach ($collection as $name => $route) {
// If the route has no _format specification, we move it to the end. If it
// does, then no match means the route is removed entirely.
if ($supported_formats = array_filter(explode('|', $route->getRequirement('_format')))) {
if (!in_array($format, $supported_formats)) {
$collection->remove($name);
}
}
else {
$collection->add($name, $route);
}
}
if (count($collection)) {
return $collection;
}
// We do not throw a
// \Symfony\Component\Routing\Exception\ResourceNotFoundException here
// because we don't want to return a 404 status code, but rather a 406.
throw new NotAcceptableHttpException(SafeMarkup::format('No route found for the specified format @format.', ['@format' => $format]));
}
}
...@@ -38,7 +38,7 @@ class NegotiationMiddleware implements HttpKernelInterface { ...@@ -38,7 +38,7 @@ class NegotiationMiddleware implements HttpKernelInterface {
* *
* @var array * @var array
*/ */
protected $formats; protected $formats = [];
/** /**
* Constructs a new NegotiationMiddleware. * Constructs a new NegotiationMiddleware.
...@@ -57,10 +57,12 @@ public function __construct(HttpKernelInterface $app, ContentNegotiation $negoti ...@@ -57,10 +57,12 @@ public function __construct(HttpKernelInterface $app, ContentNegotiation $negoti
* {@inheritdoc} * {@inheritdoc}
*/ */
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) { public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) {
// Register available mime types.
foreach ($this->formats as $format => $mime_type) { foreach ($this->formats as $format => $mime_type) {
$request->setFormat($format, $mime_type); $request->setFormat($format, $mime_type);
} }
// Determine the request format using the negotiator.
$request->setRequestFormat($this->negotiator->getContentType($request)); $request->setRequestFormat($this->negotiator->getContentType($request));
return $this->app->handle($request, $type, $catch); return $this->app->handle($request, $type, $catch);
} }
......
...@@ -83,11 +83,18 @@ ...@@ -83,11 +83,18 @@
var pb = this; var pb = this;
// When doing a post request, you need non-null data. Otherwise a // When doing a post request, you need non-null data. Otherwise a
// HTTP 411 or HTTP 406 (with Apache mod_security) error may result. // HTTP 411 or HTTP 406 (with Apache mod_security) error may result.
var uri = this.uri;
if (uri.indexOf('?') === -1) {
uri += '?';
}
else {
uri += '&';
}
uri += '_format=json';
$.ajax({ $.ajax({
type: this.method, type: this.method,
url: this.uri, url: uri,
data: '', data: '',
dataType: 'json',
success: function (progress) { success: function (progress) {
// Display errors. // Display errors.
if (progress.status === 0) { if (progress.status === 0) {
......
...@@ -285,7 +285,7 @@ public function testBlockContextualLinks() { ...@@ -285,7 +285,7 @@ public function testBlockContextualLinks() {
// Get server-rendered contextual links. // Get server-rendered contextual links.
// @see \Drupal\contextual\Tests\ContextualDynamicContextTest:renderContextualLinks() // @see \Drupal\contextual\Tests\ContextualDynamicContextTest:renderContextualLinks()
$post = array('ids[0]' => $id, 'ids[1]' => $cached_id); $post = array('ids[0]' => $id, 'ids[1]' => $cached_id);
$response = $this->drupalPost('contextual/render', 'application/json', $post, array('query' => array('destination' => 'test-page'))); $response = $this->drupalPostWithFormat('contextual/render', 'json', $post, array('query' => array('destination' => 'test-page')));
$this->assertResponse(200); $this->assertResponse(200);
$json = Json::decode($response); $json = Json::decode($response);
$this->assertIdentical($json[$id], '<ul class="contextual-links"><li class="block-configure"><a href="' . base_path() . 'admin/structure/block/manage/' . $block->id() . '">Configure block</a></li><li class="entityviewedit-form"><a href="' . base_path() . 'admin/structure/views/view/test_view_block/edit/block_1">Edit view</a></li></ul>'); $this->assertIdentical($json[$id], '<ul class="contextual-links"><li class="block-configure"><a href="' . base_path() . 'admin/structure/block/manage/' . $block->id() . '">Configure block</a></li><li class="entityviewedit-form"><a href="' . base_path() . 'admin/structure/views/view/test_view_block/edit/block_1">Edit view</a></li></ul>');
......
...@@ -55,7 +55,7 @@ protected function setUp() { ...@@ -55,7 +55,7 @@ protected function setUp() {
* Test comment row. * Test comment row.
*/ */
public function testCommentRestExport() { public function testCommentRestExport() {
$this->drupalGet(sprintf('node/%d/comments', $this->nodeUserCommented->id()), [], ['Accept' => 'application/hal+json']); $this->drupalGetWithFormat(sprintf('node/%d/comments', $this->nodeUserCommented->id()), 'hal_json');
$this->assertResponse(200); $this->assertResponse(200);
$contents = Json::decode($this->getRawContent()); $contents = Json::decode($this->getRawContent());
$this->assertEqual($contents[0]['subject'], 'How much wood would a woodchuck chuck'); $this->assertEqual($contents[0]['subject'], 'How much wood would a woodchuck chuck');
......
...@@ -386,6 +386,7 @@ display: ...@@ -386,6 +386,7 @@ display:
uses_fields: false uses_fields: false
formats: formats:
json: json json: json
hal_json: hal_json
row: row:
type: data_field type: data_field
options: options:
......
...@@ -904,7 +904,7 @@ protected function renderContextualLinks($ids, $current_path) { ...@@ -904,7 +904,7 @@ protected function renderContextualLinks($ids, $current_path) {
for ($i = 0; $i < count($ids); $i++) { for ($i = 0; $i < count($ids); $i++) {
$post['ids[' . $i . ']'] = $ids[$i]; $post['ids[' . $i . ']'] = $ids[$i];
} }
return $this->drupalPost('contextual/render', 'application/json', $post, array('query' => array('destination' => $current_path))); return $this->drupalPostWithFormat('contextual/render', 'json', $post, array('query' => array('destination' => $current_path)));
} }
/** /**
......
...@@ -181,6 +181,6 @@ protected function renderContextualLinks($ids, $current_path) { ...@@ -181,6 +181,6 @@ protected function renderContextualLinks($ids, $current_path) {
for ($i = 0; $i < count($ids); $i++) { for ($i = 0; $i < count($ids); $i++) {
$post['ids[' . $i . ']'] = $ids[$i]; $post['ids[' . $i . ']'] = $ids[$i];
} }
return $this->drupalPost('contextual/render', 'application/json', $post, array('query' => array('destination' => $current_path))); return $this->drupalPostWithFormat('contextual/render', 'json', $post, array('query' => array('destination' => $current_path)));
} }
} }
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
namespace Drupal\dblog\Tests\Rest; namespace Drupal\dblog\Tests\Rest;
use Drupal\Component\Serialization\Json; use Drupal\Component\Serialization\Json;
use Drupal\Core\Url;
use Drupal\rest\Tests\RESTTestBase; use Drupal\rest\Tests\RESTTestBase;
/** /**
...@@ -45,7 +46,7 @@ public function testWatchdog() { ...@@ -45,7 +46,7 @@ public function testWatchdog() {
$account = $this->drupalCreateUser(array('restful get dblog')); $account = $this->drupalCreateUser(array('restful get dblog'));
$this->drupalLogin($account); $this->drupalLogin($account);
$response = $this->httpRequest("dblog/$id", 'GET', NULL, $this->defaultMimeType); $response = $this->httpRequest(Url::fromRoute('rest.dblog.GET.' . $this->defaultFormat, ['id' => $id, '_format' => $this->defaultFormat]), 'GET');
$this->assertResponse(200); $this->assertResponse(200);
$this->assertHeader('content-type', $this->defaultMimeType); $this->assertHeader('content-type', $this->defaultMimeType);
$log = Json::decode($response); $log = Json::decode($response);
...@@ -54,7 +55,7 @@ public function testWatchdog() { ...@@ -54,7 +55,7 @@ public function testWatchdog() {
$this->assertEqual($log['message'], 'Test message', 'Log message text is correct.'); $this->assertEqual($log['message'], 'Test message', 'Log message text is correct.');
// Request an unknown log entry. // Request an unknown log entry.
$response = $this->httpRequest("dblog/9999", 'GET', NULL, $this->defaultMimeType); $response = $this->httpRequest(Url::fromRoute('rest.dblog.GET.' . $this->defaultFormat, ['id' => 9999, '_format' => $this->defaultFormat]), 'GET');
$this->assertResponse(404); $this->assertResponse(404);
$decoded = Json::decode($response); $decoded = Json::decode($response);
$this->assertEqual($decoded['error'], 'Log entry with ID 9999 was not found', 'Response message is correct.'); $this->assertEqual($decoded['error'], 'Log entry with ID 9999 was not found', 'Response message is correct.');
......
...@@ -409,7 +409,7 @@ function testSwitchingSecurity() { ...@@ -409,7 +409,7 @@ function testSwitchingSecurity() {
'value' => self::$sampleContent, 'value' => self::$sampleContent,
'original_format_id' => $case['format'], 'original_format_id' => $case['format'],
); );
$response = $this->drupalPost('editor/filter_xss/' . $format, 'application/json', $post); $response = $this->drupalPostWithFormat('editor/filter_xss/' . $format, 'json', $post);
$this->assertResponse(200); $this->assertResponse(200);
$json = Json::decode($response); $json = Json::decode($response);
$this->assertIdentical($json, $expected_filtered_value, 'The value was correctly filtered for XSS attack vectors.'); $this->assertIdentical($json, $expected_filtered_value, 'The value was correctly filtered for XSS attack vectors.');
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
namespace Drupal\editor\Tests; namespace Drupal\editor\Tests;
use Drupal\Component\Serialization\Json; use Drupal\Component\Serialization\Json;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\simpletest\WebTestBase; use Drupal\simpletest\WebTestBase;
/** /**
...@@ -88,7 +89,7 @@ public function testUsersWithoutPermission() { ...@@ -88,7 +89,7 @@ public function testUsersWithoutPermission() {
$this->assertRaw('<p>Do you also love Drupal?</p><figure class="caption caption-img"><img src="druplicon.png" /><figcaption>Druplicon</figcaption></figure>'); $this->assertRaw('<p>Do you also love Drupal?</p><figure class="caption caption-img"><img src="druplicon.png" /><figcaption>Druplicon</figcaption></figure>');
// Retrieving the untransformed text should result in an empty 403 response. // Retrieving the untransformed text should result in an empty 403 response.
$response = $this->drupalPost('editor/' . 'node/1/body/en/full', 'application/vnd.drupal-ajax', array()); $response = $this->drupalPost('editor/' . 'node/1/body/en/full', '', array(), array('query' => array(MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax')));
$this->assertResponse(403); $this->assertResponse(403);
$this->assertIdentical('{}', $response); $this->assertIdentical('{}', $response);
} }
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
/** /**
* Encodes HAL data in JSON. * Encodes HAL data in JSON.
* *
* Simply respond to application/hal+json requests using the JSON encoder. * Simply respond to hal_json format requests using the JSON encoder.
*/ */
class JsonEncoder extends SymfonyJsonEncoder { class JsonEncoder extends SymfonyJsonEncoder {
......
...@@ -19,7 +19,7 @@ class HalServiceProvider implements ServiceModifierInterface { ...@@ -19,7 +19,7 @@ class HalServiceProvider implements ServiceModifierInterface {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function alter(ContainerBuilder $container) { public function alter(ContainerBuilder $container) {
if ($container->has('http_middleware.negotiation')) { if ($container->has('http_middleware.negotiation') && is_a($container->getDefinition('http_middleware.negotiation')->getClass(), '\Drupal\Core\StackMiddleware\NegotiationMiddleware', TRUE)) {
$container->getDefinition('http_middleware.negotiation')->addMethodCall('registerFormat', ['hal_json', ['application/hal+json']]); $container->getDefinition('http_middleware.negotiation')->addMethodCall('registerFormat', ['hal_json', ['application/hal+json']]);
} }
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
namespace Drupal\hal\Normalizer; namespace Drupal\hal\Normalizer;
use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\rest\LinkManager\LinkManagerInterface; use Drupal\rest\LinkManager\LinkManagerInterface;
...@@ -195,14 +196,19 @@ public function denormalize($data, $class, $format = NULL, array $context = arra ...@@ -195,14 +196,19 @@ public function denormalize($data, $class, $format = NULL, array $context = arra
/** /**
* Constructs the entity URI. * Constructs the entity URI.
* *
* @param $entity * @param \Drupal\Core\Entity\EntityInterface
* The entity. * The entity.
*
* @return string * @return string
* The entity URI. * The entity URI.
*/ */
protected function getEntityUri($entity) { protected function getEntityUri(EntityInterface $entity) {
return $entity->url('canonical', array('absolute' => TRUE)); // Some entity types don't provide a canonical link template, at least call
// out to ->url().
if ($entity->isNew() || !$entity->hasLinkTemplate('canonical')) {
return $entity->url('canonical', []);
}
$url = $entity->urlInfo('canonical', ['absolute' => TRUE]);
return $url->setRouteParameter('_format', 'hal_json')->toString();
} }
/** /**
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
namespace Drupal\hal\Tests; namespace Drupal\hal\Tests;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Url; use Drupal\Core\Url;
/** /**
...@@ -169,14 +170,15 @@ public function testNormalize() { ...@@ -169,14 +170,15 @@ public function testNormalize() {
/** /**
* Constructs the entity URI. * Constructs the entity URI.
* *
* @param $entity * @param \Drupal\Core\Entity\EntityInterface $entity
* The entity. * The entity.
* *
* @return string * @return string
* The entity URI. * The entity URI.
*/ */
protected function getEntityUri($entity) { protected function getEntityUri(EntityInterface $entity) {
return $entity->url('canonical', array('absolute' => TRUE)); $url = $entity->urlInfo('canonical', ['absolute' => TRUE]);
return $url->setRouteParameter('_format', 'hal_json')->toString();
} }
} }
...@@ -79,15 +79,16 @@ function testPageCacheTags() { ...@@ -79,15 +79,16 @@ function testPageCacheTags() {
} }
/** /**
* Tests support for different cache items with different Accept headers. * Tests support for different cache items with different request formats
* specified via a query parameter.
*/ */
function testAcceptHeaderRequests() { function testQueryParameterFormatRequests() {
$config = $this->config('system.performance'); $config = $this->config('system.performance');
$config->set('cache.page.max_age', 300); $config->set('cache.page.max_age', 300);
$config->save(); $config->save();
$accept_header_cache_url = Url::fromRoute('system_test.page_cache_accept_header'); $accept_header_cache_url = Url::fromRoute('system_test.page_cache_accept_header');
$json_accept_header = array('Accept: application/json'); $accept_header_cache_url_with_json = Url::fromRoute('system_test.page_cache_accept_header', ['_format' => 'json']);
$this->drupalGet($accept_header_cache_url); $this->drupalGet($accept_header_cache_url);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'HTML page was not yet cached.'); $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'HTML page was not yet cached.');
...@@ -95,9 +96,9 @@ function testAcceptHeaderRequests() { ...@@ -95,9 +96,9 @@ function testAcceptHeaderRequests() {
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'HTML page was cached.'); $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'HTML page was cached.');
$this->assertRaw('<p>oh hai this is html.</p>', 'The correct HTML response was returned.'); $this->assertRaw('<p>oh hai this is html.</p>', 'The correct HTML response was returned.');
$this->drupalGet($accept_header_cache_url, array(), $json_accept_header); $this->drupalGet($accept_header_cache_url_with_json);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Json response was not yet cached.'); $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Json response was not yet cached.');
$this->drupalGet($accept_header_cache_url, array(), $json_accept_header); $this->drupalGet($accept_header_cache_url_with_json);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Json response was cached.'); $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.'); $this->assertRaw('{"content":"oh hai this is json"}', 'The correct Json response was returned.');
...@@ -105,8 +106,8 @@ function testAcceptHeaderRequests() { ...@@ -105,8 +106,8 @@ function testAcceptHeaderRequests() {
\Drupal::service('module_installer')->install(['node', 'rest', 'hal']); \Drupal::service('module_installer')->install(['node', 'rest', 'hal']);
$this->drupalCreateContentType(['type' => 'article']); $this->drupalCreateContentType(['type' => 'article']);
$node = $this->drupalCreateNode(['type' => 'article']); $node = $this->drupalCreateNode(['type' => 'article']);
$node_uri = 'node/' . $node->id(); $node_uri = $node->urlInfo();
$hal_json_accept_header = ['Accept: application/hal+json']; $node_url_with_hal_json_format = $node->urlInfo('canonical')->setRouteParameter('_format', 'hal_json');