Commit 0f5eab10 authored by xjm's avatar xjm

Issue #2854560 by Wim Leers, boaloysius, dawehner:...

Issue #2854560 by Wim Leers, boaloysius, dawehner: \Drupal\Core\Routing\RequestFormatRouteFilter::filter() is too HTML-centric
parent ef4f8837
......@@ -23,7 +23,10 @@ public function applies(Route $route) {
* {@inheritdoc}
*/
public function filter(RouteCollection $collection, Request $request) {
$format = $request->getRequestFormat('html');
// Determine the request format.
$default_format = static::getDefaultFormat($collection);
$format = $request->getRequestFormat($default_format);
/** @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
......@@ -48,4 +51,29 @@ public function filter(RouteCollection $collection, Request $request) {
throw new NotAcceptableHttpException("No route found for the specified format $format.");
}
/**
* Determines the default request format.
*
* By default, use 'html' as the default format. But when there's only a
* single route match, and that route specifies a '_format' requirement
* listing a single format, then use that as the default format.
*
* @param \Symfony\Component\Routing\RouteCollection $collection
* The route collection to filter.
*
* @return string
* The default format.
*/
protected static function getDefaultFormat(RouteCollection $collection) {
$default_format = 'html';
if ($collection->count() === 1) {
$only_route = $collection->getIterator()->current();
$required_format = $only_route->getRequirement('_format');
if (strpos($required_format, '|') === FALSE) {
$default_format = $required_format;
}
}
return $default_format;
}
}
......@@ -5,6 +5,7 @@
use Drupal\Core\Routing\RequestFormatRouteFilter;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
......@@ -35,10 +36,20 @@ public function testAppliesWithFormat() {
/**
* @covers ::filter
* @dataProvider filterProvider
*/
public function testFilter() {
public function testFilter(RouteCollection $collection, $request_format, array $expected_filtered_collection) {
$route_filter = new RequestFormatRouteFilter();
$request = new Request();
$request->setRequestFormat($request_format);
$collection = $route_filter->filter($collection, $request);
$this->assertCount(count($expected_filtered_collection), $collection);
$this->assertSame($expected_filtered_collection, array_keys($collection->all()));
}
public function filterProvider() {
$route_without_format = new Route('/test');
$route_with_format = $route = new Route('/test');
$route_with_format->setRequirement('_format', 'json');
......@@ -50,13 +61,16 @@ public function testFilter() {
$collection->add('test_1', $route_with_format);
$collection->add('test_2', $route_with_multiple_formats);
$request = new Request();
$request->setRequestFormat('xml');
$collection = $route_filter->filter($collection, $request);
$sole_route_match_single_format = new RouteCollection();
$sole_route_match_single_format->add('sole_route_single_format', $route_with_format);
$this->assertCount(2, $collection);
$this->assertEquals(array_keys($collection->all())[0], 'test_2');
$this->assertEquals(array_keys($collection->all())[1], 'test_0');
return [
'xml requested' => [clone $collection, 'xml', ['test_2', 'test_0']],
'json requested' => [clone $collection, 'json', ['test_1', 'test_2', 'test_0']],
'html format requested' => [clone $collection, 'html', ['test_0']],
'no format requested, defaults to html' => [clone $collection, NULL, ['test_0']],
'no format requested, single route match with single format, defaults to that format' => [clone $sole_route_match_single_format, NULL, ['sole_route_single_format']],
];
}
/**
......@@ -77,4 +91,20 @@ public function testNoRouteFound() {
$route_filter->filter($collection, $request);
}
/**
* @covers ::filter
*/
public function testNoRouteFoundWhenNoRequestFormatAndSingleRouteWithMultipleFormats() {
$this->setExpectedException(NotAcceptableHttpException::class, 'No route found for the specified format html.');
$collection = new RouteCollection();
$route_with_format = $route = new Route('/test');
$route_with_format->setRequirement('_format', 'json|xml');
$collection->add('sole_route_multiple_formats', $route_with_format);
$request = Request::create('test', 'GET');
$route_filter = new RequestFormatRouteFilter();
$route_filter->filter($collection, $request);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment