4

Within a symfony5 controller, I can return json responses via:

 return $this->json(['key' => 'content');

Yet when I throw an HttpException, I see the default html error page in both dev and production.

I want to create a restful api, so I want to convert all HttpExceptions into json.

I want to configure all my controllers to format their response as json. At most, I want to add one Exception handler that would transform the excpetions into proper messages. (In prod it should have less information, in dev it may contain the exception stacktrace.)

How can I achieve this? I thought I could use the format option of the @Route annotation but it doesn't work.

This is my example controller:

<?php declare(strict_types=1);

namespace App\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;

class StatusController extends AbstractController
{
    /**
     * @Route("/status", name="status", format="json")
     * @Template
     * @return JsonResponse
     */
    public function status()
    {
        if (true) {
            // this will render as html, how to serialize it as json?
            throw new NotFoundHttpException("This is an example");
        }


        $ok = new \stdClass();
        $ok->status = "OK";

        return $this->json($ok);
    }
}

While looking for this I came across this PR which seems to achieve what I am trying to do, yet I am unsure what I am missing.

On the symfony blog I found following answer by Yonel Ceruto saying

you will need to install/enable the serializer component,

yet I have no idea what this entails.


In dev and prod I got these html views instead of a json response:

prod

prod view of html for rendered exceptions

dev

dev view of html view for rendered exceptions

k0pernikus
  • 60,309
  • 67
  • 216
  • 347

3 Answers3

7

Turns out all I was missing was installing the serializer-pack as pointed out in the symfony docs:

composer require symfony/serializer-pack

Afterwards, my exceptions render as json fine.

k0pernikus
  • 60,309
  • 67
  • 216
  • 347
1

Create a event listener ExceptionListener and register in services.yml

services:
    ...
    App\EventListener\ExceptionListener:
        tags:
            - { name: kernel.event_listener, event: kernel.exception }

ExceptionLister.php

// src/EventListener/ExceptionListener.php
namespace App\EventListener;

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;

class ExceptionListener
{
    public function onKernelException(ExceptionEvent $event)
    {
        // You get the exception object from the received event
        $exception = $event->getThrowable();
        // Get incoming request
        $request   = $event->getRequest();

        // Check if it is a rest api request
        if ('application/json' === $request->headers->get('Content-Type'))
        {

            // Customize your response object to display the exception details
            $response = new JsonResponse([
                'message'       => $exception->getMessage(),
                'code'          => $exception->getCode(),
                'traces'        => $exception->getTrace()
            ]);

            // HttpExceptionInterface is a special type of exception that
            // holds status code and header details
            if ($exception instanceof HttpExceptionInterface) {
                $response->setStatusCode($exception->getStatusCode());
                $response->headers->replace($exception->getHeaders());
            } else {
                $response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);
            }

            // sends the modified response object to the event
            $event->setResponse($response);
        }
    }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Pascal Tovohery
  • 888
  • 7
  • 19
0

The package symfony/serializer-pack not work in my environment.

Finally I create A ErrorController to response json .

namespace App\Controller;

use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Throwable;

class JsonErrorController
{
    public function show(Throwable $exception, LoggerInterface $logger)
    {
        return new JsonResponse($exception->getMessage(), $exception->getCode());
    }
}