Search code examples
fosrestbundlesymfony

Symfony2 + FOSRestBundle + FrameworkExtraBundle Disable rest and error when using @Template


I am trying to setup my application so the same action can return responses for both web and api. This working great but sometimes the action is only required for the website and not the api. I am looking for a way to enable/disable this functionality per action/controller.

I've created two demo actions to test with, but have been unable to make one not respond on the api url.

Website is accessible via domain.com

Api is accessible via api.domain.com - All requests to the api are formatted as json using the format listener in the config

- { path: '^/', host: 'api.domain.dev', priorities: ['json'], fallback_format: ~, prefer_extension: false }

The first action(indexAction) works fine returning both HTML and Json depending on which domain you use. The problem is with the second action(testAction) and I want this to only work on the website domain. When visiting api.domain.com/company-test the response is the template HTML with the content-type header set to application/json.

Is there a way to make this return an error(404?) when the JSON version is not available and @Template is being used?

Test Actions

use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\View\View;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;

/**
 * Get a list of companies
 *
 * @Rest\Get("/company")
 * @Rest\View(
 *     template = "CompanyBundle:Company:index.html.twig",
 *     templateVar = "companies",
 *     statusCode = 200,
 * )
 *
 * @ApiDoc(
 *     description=""
 * )
 */
public function indexAction() {
    $em = $this->getDoctrine()->getManager();

    $companies = $em->getRepository('CompanyBundle:Company')->findAll();

    $view = View::create();

    $view->setData($companies);
    $view->setTemplateData(['extraData' => 'Hello World!']);
    return $view;
}


/**
 * Get a list of companies
 *
 * @Route("/company-test")
 *
 * @ApiDoc(
 *     description=""
 * )
 *
 * @Template(
 *     template="CompanyBundle:Company:index.html.twig"
 * )
 */
public function testAction() {
    $em = $this->getDoctrine()->getManager();

    $companies = $em->getRepository('CompanyBundle:Company')->findAll();

    return [
        'companies' => $companies,
        'test-test' => 'Hello World!'
    ];
}

Solution

  • I think you can extend TemplateListener from SensioFrameworkExtraBundle and modify "onKernelView" https://github.com/sensiolabs/SensioFrameworkExtraBundle/blob/master/EventListener/TemplateListener.php#L77

    Create you own listener as MyBundle\EvetListener\MyListener.php that extends original TemplateListener, then you can whole onKernelView method and chanage https://github.com/sensiolabs/SensioFrameworkExtraBundle/blob/master/EventListener/TemplateListener.php#L83 to

        if (null === $template) {
             return;
        } elseif($this->getRequest()->getHost() == $this->container->getParameter('api_domain')) { // %api_domain% should be filled at parameters.yml    
             $event->setResponse(new JsonResponse(NULL, Response::HTTP_NOT_FOUND)); // don't forget to add "use Symfony\Component\HttpFoundation\JsonResponse"
            return;
        }
    

    override the service parameter https://github.com/sensiolabs/SensioFrameworkExtraBundle/blob/master/Resources/config/view.xml#L8

    as

    <parameters>
            <parameter key="sensio_framework_extra.view.listener.class">MyApp\EventListener\MyListener</parameter>
     </parameters>
    

    or via yml if you use yml instead of xml for services config