user2143356
user2143356

Reputation: 5597

Show a specific route instead of error page (404) - Symfony2

Instead of an error page / 404 I want to just show the /sitemap page. Of course I don't want a redirect and I still want the 404 HTTP response header to be set.

Is this possible? All I can see is how to set templates in Twig.

I definitely don't want a redirect.

Upvotes: 11

Views: 11108

Answers (3)

Thomas Landauer
Thomas Landauer

Reputation: 8355

The easiest way is to create this file:

  • Symfony 3: /app/Resources/TwigBundle/views/Exception/error404.html.twig
  • Symfony 4: /templates/bundles/TwigBundle/Exception/error404.html.twig

... and copy this line into it:

{{ render(controller('App\\Controller\\HomepageController::index')) }}

That's it. This will show the home page with a 404 status code. It only works in prod environment (clear the cache!), since in dev and test Symfony always shows its exception page.

References:

Upvotes: 0

mario
mario

Reputation: 624

I have a simple web app composed by several Symfony3 bundles, all of whose controllers extend a BaseController. I have a AdminBundle too. To show a given page for every not existent url, I hacked (and made simpler) the original ExceptionController.php (from the TwigBundle):

<?php

namespace MyApp\AdminBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use MyApp\Controller\BaseController;

use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;

class ExceptionController extends BaseController
{
    /**
     * Converts an Exception to a Response.
     *
     * @param Request              $request   The request
     * @param FlattenException     $exception A FlattenException instance
     * @param DebugLoggerInterface $logger    A DebugLoggerInterface instance
     *
     * @return Response
     * @throws \InvalidArgumentException When the exception template does not exist
     */
    public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null)
    {
        $currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1));
        $code = $exception->getStatusCode();
        return $this->render('MyappAdminBundle:Admin:error.html.twig',
            array(
                'status_code' => $code,
                'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
                'exception' => $exception,
                'logger' => $logger,
                'currentContent' => $currentContent,
            )
        );
    }

    /**
     * @param int $startObLevel
     *
     * @return string
     */
    protected function getAndCleanOutputBuffering($startObLevel)
    {
        if (ob_get_level() <= $startObLevel) {
            return '';
        }

        Response::closeOutputBuffers($startObLevel + 1, true);

        return ob_get_clean();
    }
}

I define my error template as I liked.

I need to add the following line within config.yml

twig:
    exception_controller: MyappAdminBundle:Exception:show

ps I am sure my code can be improved, but it works fine.

Upvotes: 0

Ramon Kleiss
Ramon Kleiss

Reputation: 1749

As shown in the Symfony Cookbook you can override error pages in two ways:

  • Overriding the templates
  • Using a custom exception controller

If you only want to show the /sitemap route on a 404 (HttpNotFoundException) exception you could override the Twig exception template by creating a new template in app/Resources/TwigBundle/views/Exception/error404.html.twig.

Another way that is not shown in the cookbook is using an event listener. When the kernel encounters an exception, a kernel.exception event is dispatched. By default, this exception is caught by the exception listening provided by Twig. You can create your own event listener which listens for the kernel.exception event and renders a page:

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

public function onKernelException(GetResponseForExceptionEvent $event)
{
    if ($event->getException() instanceof NotFoundHttpException) {
        $response = $this->templating->renderResponse(/* sitemap */);

        $event->setResponse($response)
    }
}

(I haven't tested this code, so you should try it yourself! And you have to inject the templating service into the event listener yourself, of course).

Upvotes: 19

Related Questions