Symfony2: create a response filter and set extra response headers

Posted on by Matthias Noback

Sometimes you need to make changes to the Response object, after it is returned by your controller, but before it is rendered as output to the client (e.g. the browser). You may want to set some extra response headers, or "completely mess up the content" of the response. You can accomplish this by creating an event listener that listens to the kernel.response event. I give you a sample event listener which changes the Content-Type header in case the requested format is "json" and the browser's accepted response format contains "text/html"; in that case, at least in my experience, the browser doesn't render the JSON string as plain text when the status code is 4xx or 5xx. So in these situations, the event listener changes the "Content-Type" to "text/plain", to be sure you always get decent output in the browser.

Put the event listener in, for example /src/Acme/DemoBundle/EventListener/ResponseListener.php:

namespace Acme\DemoBundle\EventListener;

use Symfony\Component\HttpKernel\Event\FilterResponseEvent;

class ResponseListener
{
    public function onKernelResponse(FilterResponseEvent $event)
    {
        $request = $event->getRequest();

        // only do something when the requested format is "json"
        if ($request->getRequestFormat() != 'json') {
            return;
        }

        // only do something when the client accepts "text/html" as response format
        if (false === strpos($request->headers->get('Accept'), 'text/html')) {
            return;
        }

        // set the "Content-Type" header of the response
        $event->getResponse()->headers->set('Content-Type', 'text/plain');
    }
}

Now define the ResponseListener as a service in /src/Acme/DemoBundle/Resources/services.xml:

<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
    <services>
        <service id="acme.filter_response_listener" class="Acme\DemoBundle\EventListener\ResponseListener">
            <tag name="kernel.event_listener" event="kernel.response" method="onKernelResponse" />
        </service>        
    </services>
</container>

Notice the "tag" tag, by which the kernel recognizes this service as an event listener. The event attribute tells the kernel which event this listener listens to. The "method" attribute tells which method should be called when the kernel.response event occurs.

In case you don't want any other events to tamper with the Response, add a call to $event->stopPropagation().

PHP Symfony2 dependency injection service container events response
Comments
This website uses MailComments: you can send your comments to this post by email. Read more about MailComments, including suggestions for writing your comments (in HTML or Markdown).
Matthias Noback

That's right, I have done things like this sometimes, to modify output to be somewhat more usable, or specific for certain HTTP clients. Good luck!

ragtek

thx very much for this!

I tried to do something similar for my ajax requests... (not sure if there's a easier or out of the box way, but at least this way is working:D )


public function onKernelResponse(FilterResponseEvent $event)
{
$request = $event->getRequest();
// only do something with ajax requests
if ($request->isXmlHttpRequest()) {
$event->getResponse()->headers->set('Content-Type', 'text/json');
return;
}
}

Victor

Hi,
I try change page title, keywords and description via response object (in liky your onKernelResponse).

How I can do it in sumfony 2 ?

P.S. In symfony 1.4 have $response->setTitle(), $response->addMeta('keywords', 'some keyqwords').

Thanks for answer!

Matthias Noback

Hi Victor. With Symfony2, these responsibilities don't belong to the Response class anymore. Modifying the page title and meta tags should be handled during the rendering of your template. With Twig, you should use blocks for this. I'm sure there is a bundle for this too (like a SEO bundle or something). Good luck!