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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With