Search code examples
phpjsonrestsymfonyfosrestbundle

How to return or display data in JSON format using FOSRestBundle


I am working in a Restful API using Symfony2 and FOSRestBundle. I have read view layer docs but is not clear to me how to handle output for API. What I want to achieve is simple: display or return or output the result as valid JSON. This is what I have at controller:

<?php

/**
 * RestAPI:       Company.
 */
namespace PDI\PDOneBundle\Controller\Rest;

use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Request\ParamFetcherInterface;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use FOS\RestBundle\Controller\Annotations\QueryParam;
use FOS\RestBundle\Controller\Annotations\Get;

class CompanyRestController extends FOSRestController
{
    /**
     * Gets all companies.
     *
     * @return array
     *
     * @ApiDoc(
     *   resource = true,
     *       https = true,
     *   description = "Gets all companies",
     *   statusCodes = {
     *      200 = "Returned when successful",
     *      400 = "Returned when errors"
     *   }
     * )
     * @Get("/api/v1/companies")
     *
     */
    public function getCompaniesAction()
    {
        $response = array();
        $em = $this->getDoctrine()->getManager();
        $entities = $em->getRepository('PDOneBundle:Company')->findAll();

        if ($entities) {
            foreach ($entities as $entity) {
                $response['companies'][] = [
                    'id' => $entity->getId(),
                    'createdAt' => $entity->getCreatedAt(),
                    'updatedAt' => $entity->getUpdatedAt(),
                    'name' => $entity->getName(),
                    'logo_url' => $entity->getLogoUrl(),
                    'division' => $entity->getDivision(),
                    'inactive' => $entity->getInactive(),
                ];
            }

            $response['status'] = 'ok';
        } else {
            $response['status'] = 'error';
        }

        return $response;
    }
}

If I try this URL: /app_dev.php/api/v1/companies.json I got 404 error:

{"code":404,"message":"No route found for \"GET\/api\/v1\/companies.json\""}

If I try this URL: https://reptool.dev/app_dev.php/api/v1/companies error turns on:

Unable to find template "". 500 Internal Server Error - InvalidArgumentException 3 linked Exceptions: Twig_Error_Loader » InvalidArgumentException » InvalidArgumentException »

I've also check FOSRestBundleByExample but didn't get much help.

What I am missing here? How do I achieve what I need? Any advice?

FOSRest Config

I forgot to add the FOSRestBundle at config.yml:

#FOSRestBundle
fos_rest:
    param_fetcher_listener: true
    body_listener: true
    format_listener:
        rules:
            - { path: ^/, priorities: [ json, html ], fallback_format: ~, prefer_extension: true }
        media_type:
            version_regex: '/(v|version)=(?P<version>[0-9\.]+)/'

    body_converter:
        enabled: true
        validate: true

    view:
        mime_types:
            json: ['application/json', 'application/json;version=1.0', 'application/json;version=1.1']
        view_response_listener: 'force'
        formats:
            xml:  false
            json: true
        templating_formats:
            html: true

    exception:
        codes:
            'Symfony\Component\Routing\Exception\ResourceNotFoundException': 404
            'Doctrine\ORM\OptimisticLockException': HTTP_CONFLICT
        messages:
            'Symfony\Component\Routing\Exception\ResourceNotFoundException': true
    allowed_methods_listener: true
    access_denied_listener:
        json: true

Solution

  • I feel your pain. I had troubles getting started as well. One important place to start is the config. Here's what I use in my implementation.

    fos_rest:
        param_fetcher_listener: true
        view:
            mime_types:
                json: ['application/json', 'application/json;version=1.0', 'application/json;version=1.1']
            view_response_listener: 'force'
            formats:
                xml:  false
                json: true
            templating_formats:
                html: true
        format_listener:
            rules:
                - { path: ^/, priorities: [ json, html ], fallback_format: ~, prefer_extension: true }
            media_type:
                version_regex: '/(v|version)=(?P<version>[0-9\.]+)/'
        exception:
            codes:
                'Symfony\Component\Routing\Exception\ResourceNotFoundException': 404
                'Doctrine\ORM\OptimisticLockException': HTTP_CONFLICT
            messages:
                'Symfony\Component\Routing\Exception\ResourceNotFoundException': true
        allowed_methods_listener: true
        access_denied_listener:
            json: true
        body_listener: true
    

    In the format_listener if you want JSON to be the default response, make sure it's set first in priorities. Otherwise your header will need to include Accept: application/json every time. This may be why you're getting a twig error as it's trying to use twig to render an HTML output.

    Also, make sure you have a serializer like http://jmsyst.com/bundles/JMSSerializerBundle installed and included in your AppKernal.

    In your controller I found it easiest to extend the FOSRestController like you did, but also return a view object instead of creating the array yourself. The serializer will handle all of that for you.

    /**
     * RestAPI:       Company.
     */
    namespace PDI\PDOneBundle\Controller\Rest;
    
    use FOS\RestBundle\Controller\FOSRestController;
    use FOS\RestBundle\Request\ParamFetcherInterface;
    use Nelmio\ApiDocBundle\Annotation\ApiDoc;
    use FOS\RestBundle\Controller\Annotations\QueryParam;
    use FOS\RestBundle\Controller\Annotations\Get;
    
    class CompanyRestController extends FOSRestController
    {
        /**
         * Gets all companies.
         *
         * @return array
         *
         * @ApiDoc(
         *   resource = true,
         *       https = true,
         *   description = "Gets all companies",
         *   statusCodes = {
         *      200 = "Returned when successful",
         *      400 = "Returned when errors"
         *   }
         * )
         * @Get("/api/v1/companies")
         *
         */
        public function getCompaniesAction()
        {
            $response = array();
            $em = $this->getDoctrine()->getManager();
            $entities = $em->getRepository('PDOneBundle:Company')->findAll();
            if(!$entities)
            {
                 return $this->view(null, 400);
            }
    
            return $this->view($entities, 200);
        }
    }
    

    I hope this helps a little.