Reading incoming JSON bodies in Symfony

Accepting JSON encoded data from requests is crucial when developing APIs. By default, Symfony will not unserialize the object as an array, and you need a little workaround to make it available as you would with any form submitted request.

We can make use of an EventSubscriber to have the JSON content always decoded and put in $request->request.

In src/EventSubscriber create a class named RequestSubscriber. This class will be registered to listen to all kernel.controller events (fired after a controller to handle the request has been found).

The method decodeJSONBody will check the content type of the request and, if of application/json type, will decode te content to an associative array and replace it inside $request->request.

This way, every time you access the Request $request object inside your controller, you can get any object property as you would with any form submitted content.

# src/EventSubscriber/RequestSubscriber.php
<?php

namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class RequestSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::CONTROLLER => 'decodeJSONBody',
        ];
    }

    public function decodeJSONBody(ControllerEvent $event): void
    {
        $request = $event->getRequest();

        if ('json' === $request->getContentType()) {
            $data = json_decode($request->getContent(), true);
            $request->request->replace(is_array($data) ? $data : []);
        }
    }
}

For reference, consider this request:

POST {{host}}/products
Content-Type: application/json

{
  "product": {
    "name": "Shoes",
    "color": {
      "id": 1
    }
  }
}

And this controller action:

public function save(Request $request, ItemService $itemService): JsonResponse
{
    $productParams = $request->request->get('product');

    $productBuilder = new ItemBuilder();
    $productBuilder->name = $productParams['name'] ?? null;
    $productBuilder->colorId = $productParams['color']['id'] ?? null;

    $product = $itemService->persistProduct($productBuilder);

    return $this->json(
        $product->toArray()
    );
}

Thank you for your time! 💎

For any comments, please send me a tweet.