Handling AngularJS POST requests in Symfony


At Qandidate.com we started using AngularJS last year and I have to say it was love at first sight! Two-way databinding, testability, dependency injection, server communication...awesome!

Did I say server communication? We use Symfony 2 (which is awesome too) for our back end API’s. Unfortunately AngularJS and Symfony do not speak the same language out-of-the-box.

In this post I will show you how we automatically decode JSON requests so we can use it with Symfony's Request object using our symfony-json-request-transformer library (or class actually).

Proof of concept

AngularJS's $http service automatically sends a Content-Type: application/json header when POSTing data, but Symfony is more of a application/x-www-form-urlencoded kind of guy.

Let's say your AngularJS app is posting a simple JSON object like:

{
     "foo": "bar"
}

Now it's really easy to decode this in your Symfony controller:

public function postAction(Request $request)
{
    $data = json_decode($request->getContent(), true);
    echo $data['foo']; // bar
}

Simple, right? Too bad we can't use the ParameterBag interface in the Request object.

If foo were optional and a sane default would be baz you would normally do:

$foo = $request->request->get('foo', 'baz');

Fortunately we can replace a request (i.e. the ParameterBag) with our decoded JSON:

public function postAction(Request $request)
{
    $data = json_decode($request->getContent(), true);
    $request->request->replace($data);

    echo $request->request->get('foo', 'baz'); // bar
}

Awesome! But that was only one controller action...

The right way

we don't like WET

Copy and pasting this code to other controllers is very WET and we like DRY!

What if I told you you could apply this to every JSON request without having to worry about it? We wrote an event listener which - when tagged as a kernel.event_listener - will:

  1. check if a request is a JSON request
  2. if so, decode the JSON
  3. populate the Request::$request object
  4. return a HTTP 400 Bad Request when something went wrong.

Check out the code at https://github.com/qandidate-labs/symfony-json-request-transformer!

Registering this event listener is really easy. Just add the following to your services.xml:

<service id="kernel.event_listener.json_request_transformer" class="Qandidate\Common\Symfony\HttpKernel\EventListener\JsonRequestTransformerListener">
    <tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="100" />
</service>

Exit through the gift shop

I have created a demo setup of AngularJS and Symfony apps to demonstrate the code I used in this post actually works. Check it out at https://github.com/qandidate-labs/symfony-json-request-transformer-example.

That's all folks!