Thanks to visit codestin.com
Credit goes to github.com

Skip to content

404s cannot be sent in production without templating installed #25844

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
junowilderness opened this issue Jan 18, 2018 · 11 comments
Closed

404s cannot be sent in production without templating installed #25844

junowilderness opened this issue Jan 18, 2018 · 11 comments

Comments

@junowilderness
Copy link
Contributor

junowilderness commented Jan 18, 2018

Q A
Bug report? no
Feature request? yes
BC Break report? no
RFC? no

Steps to reproduce:

Create a project:

composer create-project symfony/skeleton symfony-skeleton

Set the app environment to prod:

sed -i -e 's/APP_ENV=dev/APP_ENV=prod/g' .env

This can be shown also with setting the APP_ENV environment variable.

Add a simple controller and a route.

Add an .htaccess file in /public as follows (we use Apache):

<IfModule mod_rewrite.c>
    Options -MultiViews
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>

Try to get "/foo". In the prod environment, ResourceNotFoundException is not caught or converted to a 404:

2018-01-18T15:53:46-05:00 [critical] Uncaught Exception: No route found for "GET
/foo" [Thu Jan 18 15:53:46.592760 2018] [php7:notice] [pid 66514] [client
::1:58156] PHP Fatal error:  Uncaught
Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException in
var/cache/prod/srcProdProjectContainerUrlMatcher.php:38\nStack trace:\n#0
vendor/symfony/routing/Matcher/UrlMatcher.php(95):
srcProdProjectContainerUrlMatcher->match('/foo')\n#1
vendor/symfony/routing/Router.php(262): Symfony\\Component\\Routing\\Matcher\\Ur
lMatcher->matchRequest(Object(Symfony\\Component\\HttpFoundation\\Request))\n#2
vendor/symfony/http-kernel/EventListener/RouterListener.php(109): Symfony\\Compo
nent\\Routing\\Router->matchRequest(Object(Symfony\\Component\\HttpFoundation\\R
equest))\n#3 vendor/symfony/event-dispatcher/EventDispatcher.php(212): Symfony\\
Component\\HttpKernel\\EventListener\\RouterListener->onKernelRequest(Object(Sym
fony\\Component\\HttpKernel\\Event\\GetResponseEvent), 'kernel.request',
Object(Symfony\\Component\\EventDispatcher\\EventDispatcher))\n#4 /Users/cjm/Si
in vendor/symfony/http-kernel/EventListener/RouterListener.php on line 139
@junowilderness junowilderness changed the title ResourceNotFoundException is not caught and converted to a NotFoundHttpException in the prod environment ResourceNotFoundException is neither caught nor converted to a NotFoundHttpException in the prod environment Jan 18, 2018
@junowilderness junowilderness changed the title ResourceNotFoundException is neither caught nor converted to a NotFoundHttpException in the prod environment ResourceNotFoundException is not converted to 404 when APP_ENV is set to "prod" in dotenv Jan 18, 2018
@junowilderness junowilderness changed the title ResourceNotFoundException is not converted to 404 when APP_ENV is set to "prod" in dotenv ResourceNotFoundException is not converted to a 404 when APP_ENV is set to "prod" in dotenv Jan 18, 2018
@junowilderness junowilderness changed the title ResourceNotFoundException is not converted to a 404 when APP_ENV is set to "prod" in dotenv ResourceNotFoundException is not converted to a 404 when APP_ENV is set to "prod" Jan 23, 2018
@junowilderness
Copy link
Contributor Author

junowilderness commented Jan 23, 2018

This seems related to or a duplicate of the closed issue #23203.

@junowilderness
Copy link
Contributor Author

It seems I am too in love with Flex and extremely lean apps. This is to be expected with no templating. The cure:

composer require template

Do have a nice day everyone!

@junowilderness
Copy link
Contributor Author

Ah, but it should be possible to return a proper 404 without needing templating, correct?

@junowilderness junowilderness changed the title ResourceNotFoundException is not converted to a 404 when APP_ENV is set to "prod" 404s cannot be sent in production without templating installed Jan 23, 2018
@junowilderness
Copy link
Contributor Author

A quick workaround to avoid installing template is to add a "catch all" route as the last defined route, such as:

routes.yaml:

catch:
    path: /{any}
    controller: App\Controller\DefaultController::catch
    requirements:
        any: ".*"

DefaultController.php:

<?php

namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;

class DefaultController
{
    public function catch()
    {
        return new Response('Page not found.', 404);
    }
}

@junowilderness
Copy link
Contributor Author

Here is the way to do the same with an event listener:

services.yaml:

services:
    App\EventListener\ExceptionListener:
        tags:
            - { name: kernel.event_listener, event: kernel.exception }
        arguments:
            $kernelEnvironment: "%kernel.environment%"

EventListener.php:

<?php

namespace App\EventListener;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class ExceptionListener
{
    private $kernelEnvironment;

    public function __construct(string $kernelEnvironment)
    {
        $this->kernelEnvironment = $kernelEnvironment;
    }

    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        $exception = $event->getException();

        if ($exception instanceof NotFoundHttpException && $this->kernelEnvironment == 'prod') {
            $message = "Page not found.";
            $response = new Response();
            $response->setContent($message);
            $response->setStatusCode($exception->getStatusCode());
            $response->headers->replace($exception->getHeaders());
            $event->setResponse($response);
        }
    }
}

@rodrigodiez
Copy link

rodrigodiez commented Feb 10, 2018

We are experiencing the same behaviour

Our api project has no need for templates. All requests that don't match a route generate 200 responses in prod environment. We would expect 404 instead

Happy to work on a patch if this is acknowledged as an issue and some guidance is provided

@junowilderness
Copy link
Contributor Author

junowilderness commented Feb 10, 2018

@rodrigodiez 200s or 500s?

This is what I have been using as a workaround:

namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\KernelInterface;

class ExceptionSubscriber implements EventSubscriberInterface
{
    private $kernel;

    public function __construct(KernelInterface $kernel)
    {
        $this->kernel = $kernel;
    }

    public static function getSubscribedEvents()
    {
        return [
          KernelEvents::EXCEPTION => [
            ['processException', 0],
          ]
        ];
    }

    public function processException(GetResponseForExceptionEvent $event)
    {
        /**
         * This is needed until Symfony can handle converting
         * HttpException without template installed.
         *
         * @see https://github.com/symfony/symfony/issues/25844
         */
        if (!$this->kernel->isDebug()) {
            $exception = $event->getException();
            $response = new Response();
            if ($exception instanceof HttpExceptionInterface) {
                $code = $exception->getStatusCode();
                $response->setStatusCode($code);
                $message = isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '';
                $response->setContent($message);
            } else {
                $response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);
            }

            $event->setResponse($response);
        }
    }
}

@rodrigodiez
Copy link

rodrigodiez commented Feb 11, 2018

@cilefen I am getting 200s

composer create-project symfony/skeleton symfony-404-bug-test 
echo "APP_ENV=prod" > symfony-404-bug-test/.env
docker run --rm -p "80:80" -v "${PWD}/symfony-404-bug-test:/var/www/html" php:7.2-apache
curl -i http://localhost/public/index.php/foo

Results in

HTTP/1.1 200 OK
Date: Sun, 11 Feb 2018 08:45:09 GMT
Server: Apache/2.4.25 (Debian)
X-Powered-By: PHP/7.2.2
Vary: Accept-Encoding
Content-Length: 1174
Content-Type: text/html; charset=UTF-8

<br />
<b>Fatal error</b>:  Uncaught Symfony\Component\Routing\Exception\ResourceNotFoundException in /var/www/html/var/cache/prod/srcProdProjectContainerUrlMatcher.php:37
Stack trace:
#0 /var/www/html/vendor/symfony/routing/Matcher/UrlMatcher.php(95): srcProdProjectContainerUrlMatcher-&gt;match('/foo')
#1 /var/www/html/vendor/symfony/routing/Router.php(262): Symfony\Component\Routing\Matcher\UrlMatcher-&gt;matchRequest(Object(Symfony\Component\HttpFoundation\Request))
#2 /var/www/html/vendor/symfony/http-kernel/EventListener/RouterListener.php(114): Symfony\Component\Routing\Router-&gt;matchRequest(Object(Symfony\Component\HttpFoundation\Request))
#3 /var/www/html/vendor/symfony/event-dispatcher/EventDispatcher.php(212): Symfony\Component\HttpKernel\EventListener\RouterListener-&gt;onKernelRequest(Object(Symfony\Component\HttpKernel\Event\GetResponseEvent), 'kernel.request', Object(Symfony\Component\EventDispatcher\EventDispatcher))
#4 /var/www/html/vendor/symfony/event-dispatcher/EventDispatcher.php(44): Symfony\Component\EventDispatcher\EventDis in <b>/var/www/html/vendor/symfony/http-kernel/EventListener/RouterListener.php</b> on line <b>144</b><br />

@nicolas-grekas
Copy link
Member

See symfony/recipes#401

nicolas-grekas added a commit that referenced this issue Apr 29, 2018
…nstalled (cilefen)

This PR was merged into the 3.4 branch.

Discussion
----------

[HttpKernel] Catch HttpExceptions when templating is not installed

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | ?
| Deprecations? | no
| Tests pass?   | ?
| Fixed tickets | #25844
| License       | MIT
| Doc PR        | symfony/symfony-docs#... <!--highly recommended for new features-->

- [x] Test manually
- [x] Check for BC breaks
- [x] Needs tests

<!--
- Bug fixes must be submitted against the lowest branch where they apply
  (lowest branches are regularly merged to upper ones so they get the fixes too).
- Features and deprecations must be submitted against the master branch.
- Replace this comment by a description of what your PR is solving.
-->

Commits
-------

4e527aa bug #25844 [HttpKernel] Catch HttpExceptions when templating is not installed
nicolas-grekas added a commit that referenced this issue Apr 30, 2018
* 3.4:
  PhpDoc: There is no attempt to create the directory
  Avoiding an error when an unused service has a missing base class
  Add an implementation just for php 7.0
  bumped Symfony version to 2.7.47
  Fix #27011: Session ini_set bug
  [Cache] TagAwareAdapterInterface::invalidateTags() should commit deferred items
  updated VERSION for 2.7.46
  update CONTRIBUTORS for 2.7.46
  updated CHANGELOG for 2.7.46
  bug #25844 [HttpKernel] Catch HttpExceptions when templating is not installed
nicolas-grekas added a commit that referenced this issue Apr 30, 2018
* 4.0:
  PhpDoc: There is no attempt to create the directory
  Avoiding an error when an unused service has a missing base class
  Add an implementation just for php 7.0
  bumped Symfony version to 2.7.47
  Fix #27011: Session ini_set bug
  [Cache] TagAwareAdapterInterface::invalidateTags() should commit deferred items
  updated VERSION for 2.7.46
  update CONTRIBUTORS for 2.7.46
  updated CHANGELOG for 2.7.46
  bug #25844 [HttpKernel] Catch HttpExceptions when templating is not installed
@nicolas-grekas
Copy link
Member

This is going to be reverted as "not a bug", and considered as a new feature instead.

@nicolas-grekas nicolas-grekas reopened this Jun 6, 2018
@nicolas-grekas nicolas-grekas added Feature and removed Bug labels Jun 6, 2018
nicolas-grekas added a commit that referenced this issue Jul 7, 2018
…HTTP status codes by default (nicolas-grekas)

This PR was merged into the 4.2-dev branch.

Discussion
----------

[HttpKernel][FrameworkBundle] Turn HTTP exceptions to HTTP status codes by default

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #25844
| License       | MIT
| Doc PR        | -

When an exception is thrown, *if it is not handled* then it will be reinjected as a 2nd exception event via `HttpKernel::terminateWithException()`. When this happens, this will generate a proper HTTP status code.

Commits
-------

80b0739 [HttpKernel][FrameworkBundle] Turn HTTP exceptions to HTTP status codes by default
@junowilderness
Copy link
Contributor Author

junowilderness commented Aug 17, 2018

Another quick workaround for those making APIs until #27519 is released...

index.php:

$kernel = new Kernel($env, $debug);
$request = Request::createFromGlobals();
try {
    $response = $kernel->handle($request);
    $response->send();
} catch (\Symfony\Component\HttpKernel\Exception\NotFoundHttpException $e) {
    $response = new \Symfony\Component\HttpFoundation\Response("404 Not Found", 404);
    $response->send();
}
$kernel->terminate($request, $response);

(edited)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants