Fosrestbundle: Unable to find template ""

Created on 27 Jan 2018  路  8Comments  路  Source: FriendsOfSymfony/FOSRestBundle

Hi,
help me to fix this issue.
The written code is working fine for rest endpoint and throws proper exception code when i access a non-existence rest end point . It also loads html correctly for regular symfony routes if exists.
now the problem occurs when i access a non existence symfony route[not rest] , expected result was not found but received Unable to find template ""

here is my config file:

fos_rest:
    body_listener: true
    body_converter:
       enabled: true
       validate: true
       validation_errors_argument: validationErrors
    param_fetcher_listener: true
    routing_loader:
       default_format: json
       include_format: false
    format_listener:
      rules:
          - { path: '^/api', priorities: [ 'json'], fallback_format: json , prefer_extension: true }
          - { path: '^/', priorities: ['text/html', '*/*'], fallback_format: html, prefer_extension: false }
    exception:
        enabled: true
       exception_controller: 'App\Exception\ExceptionController::showAction'
    view:
       view_response_listener: 'force'
    serializer:
       groups: ['Default']

I am using symfony4

Most helpful comment

I'm late to the party but the normal Goog search lead me here. I have a custom Exception controller for my API exceptions, (I want to follow https://jsonapi.org/format/#document-structure) but I also want to have exceptions rendered with twig for routes/controllers that are not api requests.

As the docs say https://symfony.com/doc/master/bundles/FOSRestBundle/4-exception-controller-support.html in the supplementary note The FOSRestBundle ExceptionController is executed before the one of the TwigBundle.

So my issue was I was getting the An instance of Symfony\Bundle\FrameworkBundle\Templating\EngineInterface must be injected in FOS\RestBundle\View\ViewHandler error when throwing an exception in a non api controller.

My workaround, since I knew that the FOSRest exception is called first, I create the Twig Exception controller on non /api route. I'm not convinced that this is correct at this point in time, seems hacky to me but I'm adding here in case it helps others. Note that I bascically copy/crib all the FOS Rest exception controller and alter as I require. Thanks

use Symfony\Bundle\TwigBundle\Controller\ExceptionController as TwigExceptionController;
use FOS\RestBundle\View\ViewHandlerInterface;

 public function __construct(
        TwigEnvironment $twigEnvironment,
        ViewHandlerInterface $viewHandler
    ) {
        $this->twigEnvironment = $twigEnvironment;
        $this->viewHandler = $viewHandler;
    }

/**
     * Converts an Exception to a Response.
     *
     * @param Request                   $request
     * @param \Exception|\Throwable     $exception
     * @param DebugLoggerInterface|null $logger
     *
     * @throws \InvalidArgumentException
     *
     * @return Response
     */
    public function showAction(Request $request, \Exception $exception, DebugLoggerInterface $logger = null)
    {
        // If we have DebugLoggerInterface, then we deem we should "show the exception
        $debug = ($logger) ? true : false;
        $currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1));
        $code = $this->getStatusCode($exception);
        $templateData = $this->getTemplateData($currentContent, $code, $exception, $logger);

        // Depending on the route, call this view or the default twig.
        if (false !== strpos($request->getPathInfo(), '/api')) {
            $view = $this->createView($templateData['exception'], $code, $templateData, $request, $debug);
            $response = $this->viewHandler->handle($view);
        } else {
            if (!$this->twigExceptionController) {
                $this->twigExceptionController = new TwigExceptionController($this->twigEnvironment, $debug);
            }
            $response = $this->twigExceptionController->showAction($request, $templateData['exception'], $logger);
        }

        return $response;
    }

Addendum

I kept reading the docs! Instead of all of the above, I just added a zone to my fos_rest.yaml so that listeners only acted on my api routes! Works a treat & I can remove my custom logic above. Note I'm usig Symony 4.3 and FosRest 2.5

https://symfony.com/doc/master/bundles/FOSRestBundle/3-listener-support.html#zone-listener

All 8 comments

Can you create a small example application that allows to reproduce your issue?

I exactly have the same issue, after requiring and setting twig as template engine for fosrest.

For informations, I had to require fosrest as dev-master to handle an issue on public services (it was not able to inject view_handler service because of its private state: https://github.com/FriendsOfSymfony/FOSRestBundle/issues/1790) and force the installation of twig for an API skeleton to handle the issue:
An instance of Symfony\Bundle\FrameworkBundle\Templating\EngineInterface must be injected in FOS\RestBundle\View\ViewHandler

And now, I also have this issue on unable to find a template "".
By the way, it's not possible to used fosrest without requiring twig ? I mean, this project will only serve json responses so it's useless to instantiate the whole twig engine just for that.

PS: here is my configuration

config/packages/fos_rest.yml

fos_rest:
    body_listener: true
    param_fetcher_listener: true
    routing_loader: true
    view:
        view_response_listener: 'force'
    format_listener:
        rules:
            - { path: ^/api, prefer_extension: true, fallback_format: json, priorities: [ json, html ] }

config/packages/framework.yml

framework:
    secret: '%env(APP_SECRET)%'
    session:
        handler_id: ~

    php_errors:
        log: true

    templating: { engines: ['twig'] }

sensio_framework_extra:
    view: { annotations: true }

Same issue on Symfony 4,
I remove the line :
exception_controller: 'App\Exception\ExceptionController::showAction'
and it solves it.

I did my own exceptioncontroller. no need for twig anymore there... would be good to get rid of it on the whole view thing is if its not necessary.
GIST for the working controller: https://gist.github.com/BigZ/4078a1a8e90d88386d8fb16863306fbe

I'm late to the party but the normal Goog search lead me here. I have a custom Exception controller for my API exceptions, (I want to follow https://jsonapi.org/format/#document-structure) but I also want to have exceptions rendered with twig for routes/controllers that are not api requests.

As the docs say https://symfony.com/doc/master/bundles/FOSRestBundle/4-exception-controller-support.html in the supplementary note The FOSRestBundle ExceptionController is executed before the one of the TwigBundle.

So my issue was I was getting the An instance of Symfony\Bundle\FrameworkBundle\Templating\EngineInterface must be injected in FOS\RestBundle\View\ViewHandler error when throwing an exception in a non api controller.

My workaround, since I knew that the FOSRest exception is called first, I create the Twig Exception controller on non /api route. I'm not convinced that this is correct at this point in time, seems hacky to me but I'm adding here in case it helps others. Note that I bascically copy/crib all the FOS Rest exception controller and alter as I require. Thanks

use Symfony\Bundle\TwigBundle\Controller\ExceptionController as TwigExceptionController;
use FOS\RestBundle\View\ViewHandlerInterface;

 public function __construct(
        TwigEnvironment $twigEnvironment,
        ViewHandlerInterface $viewHandler
    ) {
        $this->twigEnvironment = $twigEnvironment;
        $this->viewHandler = $viewHandler;
    }

/**
     * Converts an Exception to a Response.
     *
     * @param Request                   $request
     * @param \Exception|\Throwable     $exception
     * @param DebugLoggerInterface|null $logger
     *
     * @throws \InvalidArgumentException
     *
     * @return Response
     */
    public function showAction(Request $request, \Exception $exception, DebugLoggerInterface $logger = null)
    {
        // If we have DebugLoggerInterface, then we deem we should "show the exception
        $debug = ($logger) ? true : false;
        $currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1));
        $code = $this->getStatusCode($exception);
        $templateData = $this->getTemplateData($currentContent, $code, $exception, $logger);

        // Depending on the route, call this view or the default twig.
        if (false !== strpos($request->getPathInfo(), '/api')) {
            $view = $this->createView($templateData['exception'], $code, $templateData, $request, $debug);
            $response = $this->viewHandler->handle($view);
        } else {
            if (!$this->twigExceptionController) {
                $this->twigExceptionController = new TwigExceptionController($this->twigEnvironment, $debug);
            }
            $response = $this->twigExceptionController->showAction($request, $templateData['exception'], $logger);
        }

        return $response;
    }

Addendum

I kept reading the docs! Instead of all of the above, I just added a zone to my fos_rest.yaml so that listeners only acted on my api routes! Works a treat & I can remove my custom logic above. Note I'm usig Symony 4.3 and FosRest 2.5

https://symfony.com/doc/master/bundles/FOSRestBundle/3-listener-support.html#zone-listener

Closing as a solution was found and the error rendering using twig templates is going to be deprecated in symfony 4.4 and the SF templating part has already been deprecated in 4.3

This is closed, but I still don't know how to achieve that the default exception page (with trace etc.) is shown in development environment for HTML responses while errors are formatted as JSON/XML for JSON/XML responses?

The zone trick of kylehq doesn't work for me, because there is no special URL pattern for HTML routes in my case. I just have HTML resources (e.g. print version of a resource) as I have JSON resources.

I could modify kylehq's ExceptionController to detect those resources, but I don't understand why this is required now. Everything worked fine as described above with Symfony 3. What changed?

Was this page helpful?
0 / 5 - 0 ratings