Sn0opr Sn0opr - 1 month ago 11
PHP Question

Override FOS Rest Bundle response

I'm using FOS Rest Bundle for my RESTful API, I want to override the responses returned from my ApiController, example:

php

/**
* @QueryParam(name="sourceAddr", strict=true, requirements="[0-9]{8}", description="Do things")
* @param $paramFetcher ParamFetcher
* @return array
* @throws MtBoxApiException
*/
public function getAuthAction(ParamFetcher $paramFetcher)
{
return [
'rfid' => '445545454',
'fName' => 'adazda',
'lName' => '8888888',
'prod' => 75
];
}


What I want is adding additional data to the returned responses, so I want to intercept these responses and override them based on some conditions.

The final result that I want the api returns:

{
someAdditionalData1: value1,
someAdditionalData2: value2,
data: {
//the data returned by the controller action
}
}

Answer

For an idea how this is done you can look at the FOS\RestBundle\EventListener\ViewResponseListener class. They register this class as an event subscriber. You can register your class in the same way in your services.yml

test_response_listener:
    class: MyBundle\EventListener\MyViewResponseListener
    tags:
        - { name: kernel.event_subscriber }

You need to ensure your class implements Symfony\Component\EventDispatcher\EventSubscriberInterface and contains the method getSubscribedEvents like this:

public static function getSubscribedEvents()
{
    return array(
        KernelEvents::VIEW => array('onKernelView', 50),
    );
}

The event is 'onKernelView', i.e. when a view is returned. This will only be called if the response from the controller is not actually a Response object. In my test of this I returned a User object so it was called. The "50" represents the priority. If you don't put it higher than 30 then the FOSRestBundle listener will be called first and will set the response. If any of these listeners call $event->setResponse then the other ones are ignored so make sure you don't do it in your method or the FOSRest one won't be called.

The onKernelView is the name of the method to be called. It will receive a certain type of event so make your method signature like this:

public function onKernelView(GetResponseForControllerResultEvent $event)

Now, finally, what you want to do is unify the response format. You can do this by changing the controller result of the event inside your listener method to match the format you want:

$event->setControllerResult([
    'foo' => 'bar',
    'data' => $event->getControllerResult()
]);

If you have the serializer set up it should still serialize your controller result as normal, but you'll get the added data in the response.