Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trigger error404.json.twig

Tags:

json

rest

symfony

I'm trying to get my customised json error template to be returned by Symfony but it keeps returning the HTML version instead. Using a rest client I have set Accept and Content-Type to "application/json" but only the contents of my error404.html.twig are returned, not the error404.json.twig file.

At the moment I am testing this via a completely invalid URL (i.e. there is no route), but once I have this working I will also be using it for valid URLs which do not lead to resource and internally the code throws a HttpNotFoundException

like image 280
Craig Avatar asked Sep 26 '22 13:09

Craig


1 Answers

Setting the headers on your request will not actually have any effect on what error template gets returned, despite seeming like that might be the logical path. The error template that gets generated is based on the request format, which is either set in the _format parameter or manually on the Request object itself. There's a solution in this post but if you're getting a 404 error, you couldn't exactly set the _format parameter or $request->setRequestFormat('json') call from a Controller in which no route existed.

Symfony has pretty good documentation how to customize error pages, including the path that you'll likely want to take, which is overriding the default ExceptionController. Essentially you would override the showAction() or findTemplate() method which would take Accept header and check for application/json. You could also check the Content-Type header as an additional requirement. So here would be the declaration of your listener:

# app/config/services.yml
services:
    app.exception_controller:
    class: AppBundle\Controller\CustomExceptionController
    arguments: ['@twig', '%kernel.debug%']

Then setting the Twig parameter:

# app/config/config.yml
twig:
    exception_controller:  app.exception_controller:showAction

Now to define your class and override the relevant parts you need. This first example overrides the showAction() by checking your Accept header and then modifying the Request format:

namespace AppBundle\Controller;

use Symfony\Bundle\TwigBundle\Controller\ExceptionController;

class CustomExceptionController extends ExceptionController
{
    /*
     * {@inheritDoc}
     */
    public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null)
    {
        if (in_array('application/json', $request->getAcceptableContentTypes())) {
            $request->setRequestFormat('json');
        }

        parent::showAction($request, $exception, $logger);
    }
}

Symfony says that the findTemplate() method is the one that locates the template to be used, so you could put the code there instead. Here's an example of overriding that function, with additional checking ofthe Content-Type header in case you require both conditions to be met:

namespace AppBundle\Controller;

use Symfony\Bundle\TwigBundle\Controller\ExceptionController;

class CustomExceptionController extends ExceptionController
{
    /*
     * {@inheritDoc}
     */
    protected function findTemplate(Request $request, $format, $code, $showException)
    {
        if (in_array('application/json', $request->getAcceptableContentTypes()) &&
            0 === strpos($request->headers->get('Content-Type'), 'application/json')
        ) {
            $format = 'json';
        }

        parent::findTemplate($request, $format, $code, $showException);
    }
}

Of course you can also create an exception listener yourself by working with the kernel.exception event for even further control.

If you use the FOSRestBundle it tries to determine the request format for you via a listener using the Request Accept header and the format priority configuration. If you use this bundle you might be able to avoid custom coding and just rely on that.

like image 200
Jason Roman Avatar answered Sep 28 '22 06:09

Jason Roman