X-Git-Url: http://aleph1.co.uk/gitweb/?a=blobdiff_plain;f=web%2Fcore%2Flib%2FDrupal%2FCore%2FRouting%2FRequestFormatRouteFilter.php;h=fad7bc04659f0ef4dedc87741a08eae75bfcb79b;hb=0bf8d09d2542548982e81a441b1f16e75873a04f;hp=547e6117e7021af97ae70ab6001c1d399f4a50e6;hpb=a2bd1bf0c2c1f1a17d188f4dc0726a45494cefae;p=yaffs-website diff --git a/web/core/lib/Drupal/Core/Routing/RequestFormatRouteFilter.php b/web/core/lib/Drupal/Core/Routing/RequestFormatRouteFilter.php index 547e6117e..fad7bc046 100644 --- a/web/core/lib/Drupal/Core/Routing/RequestFormatRouteFilter.php +++ b/web/core/lib/Drupal/Core/Routing/RequestFormatRouteFilter.php @@ -2,6 +2,7 @@ namespace Drupal\Core\Routing; +use Drupal\Core\Url; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException; use Symfony\Component\Routing\Route; @@ -10,14 +11,7 @@ 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'); - } +class RequestFormatRouteFilter implements FilterInterface { /** * {@inheritdoc} @@ -25,30 +19,60 @@ class RequestFormatRouteFilter implements RouteFilterInterface { public function filter(RouteCollection $collection, Request $request) { // Determine the request format. $default_format = static::getDefaultFormat($collection); - $format = $request->getRequestFormat($default_format); + // If the request does not specify a format then use the default. + if (is_null($request->getRequestFormat(NULL))) { + $format = $default_format; + $request->setRequestFormat($default_format); + } + else { + $format = $request->getRequestFormat($default_format); + } + $routes_with_requirement = []; + $routes_without_requirement = []; + $result_collection = new RouteCollection(); /** @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); - } + if (!$route->hasRequirement('_format')) { + $routes_without_requirement[$name] = $route; + continue; } else { - $collection->add($name, $route); + $routes_with_requirement[$name] = $route; } } - if (count($collection)) { - return $collection; + foreach ($routes_with_requirement 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')))) && in_array($format, $supported_formats, TRUE)) { + $result_collection->add($name, $route); + } + } + + foreach ($routes_without_requirement as $name => $route) { + $result_collection->add($name, $route); + } + + if (count($result_collection)) { + return $result_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("No route found for the specified format $format."); + $available_formats = static::getAvailableFormats($collection); + $not_acceptable = new NotAcceptableHttpException("No route found for the specified format $format. Supported formats: " . implode(', ', $available_formats) . '.'); + if ($available_formats) { + $links = []; + foreach ($available_formats as $available_format) { + $url = Url::fromUri($request->getUri(), ['query' => ['_format' => $available_format]])->toString(TRUE)->getGeneratedUrl(); + $content_type = $request->getMimeType($available_format); + $links[] = "<$url>; rel=\"alternate\"; type=\"$content_type\""; + } + $not_acceptable->setHeaders(['Link' => implode(', ', $links)]); + } + throw $not_acceptable; } /** @@ -56,7 +80,9 @@ class RequestFormatRouteFilter implements RouteFilterInterface { * * 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. + * listing a single format, then use that as the default format. Also, if + * there are multiple routes which all require the same single format then + * use it. * * @param \Symfony\Component\Routing\RouteCollection $collection * The route collection to filter. @@ -65,15 +91,32 @@ class RequestFormatRouteFilter implements RouteFilterInterface { * 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; + $formats = static::getAvailableFormats($collection); + + // The default format is 'html' unless ALL routes require the same format. + return count($formats) === 1 + ? reset($formats) + : 'html'; + } + + /** + * Gets the set of formats across all routes in the collection. + * + * @param \Symfony\Component\Routing\RouteCollection $collection + * The route collection to filter. + * + * @return string[] + * All available formats. + */ + protected static function getAvailableFormats(RouteCollection $collection) { + $all_formats = array_reduce($collection->all(), function (array $carry, Route $route) { + // Routes without a '_format' requirement are assumed to require HTML. + $route_formats = !$route->hasRequirement('_format') + ? ['html'] + : explode('|', $route->getRequirement('_format')); + return array_merge($carry, $route_formats); + }, []); + return array_unique(array_filter($all_formats)); } }