From 6f5c9ab80b0a963badbc8137b873e2a7f1e063e2 Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: Fri, 29 Jan 2021 12:00:10 +0100 Subject: [PATCH] Show full URI when route not found When accessing a route that does not exist, Symfony throws a `NotFoundHttpException` that says `No route found for "POST /path"`. On some projects this might be good enough to find the root cause, but on projects that have lots of routes on different hosts, it becomes hard to understand how the request was initiated. Was it done over HTTP or HTTPS? What was the hostname? Did the user specify a port? To make this easier, we now show the full URI of the path, like this: `No route found for "POST https://www.symfony.com/path"`. --- .../EventListener/RouterListener.php | 4 +- .../EventListener/RouterListenerTest.php | 57 +++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php index c10897a9e2..9c36f7cb58 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php @@ -127,7 +127,7 @@ class RouterListener implements EventSubscriberInterface unset($parameters['_route'], $parameters['_controller']); $request->attributes->set('_route_params', $parameters); } catch (ResourceNotFoundException $e) { - $message = sprintf('No route found for "%s %s"', $request->getMethod(), $request->getPathInfo()); + $message = sprintf('No route found for "%s %s"', $request->getMethod(), $request->getUriForPath($request->getPathInfo())); if ($referer = $request->headers->get('referer')) { $message .= sprintf(' (from "%s")', $referer); @@ -135,7 +135,7 @@ class RouterListener implements EventSubscriberInterface throw new NotFoundHttpException($message, $e); } catch (MethodNotAllowedException $e) { - $message = sprintf('No route found for "%s %s": Method Not Allowed (Allow: %s)', $request->getMethod(), $request->getPathInfo(), implode(', ', $e->getAllowedMethods())); + $message = sprintf('No route found for "%s %s": Method Not Allowed (Allow: %s)', $request->getMethod(), $request->getUriForPath($request->getPathInfo()), implode(', ', $e->getAllowedMethods())); throw new MethodNotAllowedHttpException($e->getAllowedMethods(), $message, $e); } diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php index cc46fd8db5..453bf5b0e3 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php @@ -24,9 +24,13 @@ use Symfony\Component\HttpKernel\EventListener\ErrorListener; use Symfony\Component\HttpKernel\EventListener\RouterListener; use Symfony\Component\HttpKernel\EventListener\ValidateRequestListener; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; use Symfony\Component\Routing\Exception\NoConfigurationException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Matcher\RequestMatcherInterface; use Symfony\Component\Routing\Matcher\UrlMatcherInterface; use Symfony\Component\Routing\RequestContext; @@ -217,4 +221,57 @@ class RouterListenerTest extends TestCase $listener = new RouterListener($requestMatcher, $this->requestStack, new RequestContext()); $listener->onKernelRequest($event); } + + public function testResourceNotFoundException() + { + $this->expectException(NotFoundHttpException::class); + $this->expectExceptionMessage('No route found for "GET https://www.symfony.com/path" (from "https://www.google.com")'); + + $context = new RequestContext(); + + $urlMatcher = $this->createMock(UrlMatcherInterface::class); + + $urlMatcher->expects($this->any()) + ->method('getContext') + ->willReturn($context); + + $urlMatcher->expects($this->any()) + ->method('match') + ->willThrowException(new ResourceNotFoundException()); + + $kernel = $this->createMock(HttpKernelInterface::class); + $request = Request::create('https://www.symfony.com/path'); + $request->headers->set('referer', 'https://www.google.com'); + + $event = new RequestEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + + $listener = new RouterListener($urlMatcher, $this->requestStack); + $listener->onKernelRequest($event); + } + + public function testMethodNotAllowedException() + { + $this->expectException(MethodNotAllowedHttpException::class); + $this->expectExceptionMessage('No route found for "GET https://www.symfony.com/path": Method Not Allowed (Allow: POST)'); + + $context = new RequestContext(); + + $urlMatcher = $this->createMock(UrlMatcherInterface::class); + + $urlMatcher->expects($this->any()) + ->method('getContext') + ->willReturn($context); + + $urlMatcher->expects($this->any()) + ->method('match') + ->willThrowException(new MethodNotAllowedException(['POST'])); + + $kernel = $this->createMock(HttpKernelInterface::class); + $request = Request::create('https://www.symfony.com/path'); + + $event = new RequestEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + + $listener = new RouterListener($urlMatcher, $this->requestStack); + $listener->onKernelRequest($event); + } }