Search code examples
jsonsymfonysymfony-2.2

Return JSON based on "Accept: application/json" from a Symfony2 controller


I've got a CRUD form generated via the SensioGeneratorBundle, as described here. This works great.

However, I would like to also return JSON, rather than HTML responses, if the "Accept" HTTP header contains only "application/json". I'm working on a prototype for a JSON service and this would help me jump start things.

I figured out that I can turn my entities into a JSON string like this:

use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;

$serializer = new Serializer(array(new GetSetMethodNormalizer()), array('json' => new 
JsonEncoder()));
$json = $serializer->serialize($entity, 'json');

However, at the end of that, $json contains a string that is my JSON data. I want to just directly output that to the requestor, rather than render the usual view. I've tried returning a new JsonResponse($json), but it re-encodes the JSON string, so it winds up double-encoded.

So I have two questions:

  1. What is the "correct" way to inspect the HTTP requestion headers? I know I can just look in $_SERVER, but I'm thinking that there may be a better way to do this in Symfony2.
  2. What is the "correct" way to return a JSON string, or to translate my entities into JSON that is returned straight to the requestor, without rendering the usual view.

Thanks!


Solution

  • This will verify if the current request is XHR and then send back properly formatted JSON data:

    public function someAction(Request $request)
    {
        if ($request->isXmlHttpRequest()) {
            $serializer = new Serializer(array(
                new GetSetMethodNormalizer()
            ), array(
                'json' => new JsonEncoder()
            ));
    
            $response = $serializer->serialize(array(
                'success' => true,
                'data' => array(
                    'entity' => $entities,
                )
            ), 'json');
    
            return new Response($response, 200, array('Content-Type' => 'application/json'));
        } else {
            // Run "normal" request code, render a view
        }
    }
    

    By the way, JMSSerializerBundle makes the serializing syntax more straightforward (because, let's face it, the native Symfony way for this is ugly) and also provides some additionnal features such as excluding entity fields to serialize (through annotations).

    With JMS, my code looks like this:

    if ($request->isXmlHttpRequest()) {
        $response = array('success' => true, 'data' => array(
            'entity' => $this->container->get('serializer')->serialize($entity, 'json'),
            'lastPage' => $lastPage,
        ));
        return new Response(json_encode($response), 200, array('Content-Type' => 'application/json'));
    }
    

    And finally, 'success' and 'data' are in no way required, it's just the structure I use to split status and data to be readable in JavaScript.