Drupal - How to receive JSON data in controller with Drupal 8?

In case you want to do it more the D8 way here is a rough outline what you could do, which allows you to be flexible without the requirement to do everything in your own. A proper rest integration requires you to work with all the different HTTP status codes, header fields etc. Rest takes partly care of that

The rest module provides a generic abstraction for REST, not tight to entities in the first place. Entities are just one example.

First you should register your rest plugin, like every other plugin:

namespace Drupal\pinguin\Plugin\rest\resource;

use Drupal\rest\Plugin\ResourceBase;

/**
 * Provides a resource for database watchdog log entries.
 *
 * @RestResource(
 *   id = "pinguin",
 *   label = @Translation("Example rest plugin for pinguins"),
 *   uri_paths = {
 *     "canonical" = "/pinguin/{id}"
 *   },
 *   serialization_class = "Drupal\pinguin\PingInterface"
 * )
 */
class Pinguin extends ResourceBase {
}

If you do an updating request like PUSH or UPDATE, it uses the serializer component to convert your data into some php array/ object. It uses two steps in order to do that. First the data incoming is decoded, for example from JSON into a PHP array. The next step is the denormalization. This step converts the denormalized data (PHP array) into a domain object, for example the Ping (a single pinguin).

The first step is to specify the resulting class in the annotation (see above). To register a new denormalization you add the following entry into your .services.yml:

services:
  pinguin.denormalizer.pinguin:
    class: Drupal\pinguin\normalizer\PinguinDenormalizer
    tags:
      - { name: normalizer }

This normalizer specifies which interface it can convert to:

<?php

namespace Drupal\pinguin\normalizer;

use \Drupal\serialization\Normalizer\NormalizerBase;

class PinguinDenormalizer extends NormalizerBase {

   // IMPORTANT
   /**
    * The interface or class that this Normalizer supports.
    *
    * @var array
    */
    protected $supportedInterfaceOrClass = array('Drupal\pinguin\PingInterface');

    /**
    * {@inheritdoc}
    */
    public function denormalize($data, $class, $format = NULL, array $context  = array()) {     
      // Validate the data.
      if (empty($data['location']) || $data['location'] == 'northpole') {
        throw new \UnexpectedValueException("this can't be real pinguins");
      }

      // create the domain object.
      return new Ping($data['name'], $data['location'], $data['family']);
    }
 }

Once this serialization is done, it calls a method named after the HTTP method on the plugin class, so for example "post". There you can do whatever you want.

<?php
class Pinguin extends ResourceBase {
  public function post(PingInterface $ping) {
    // deal with the data, save it for example.
    $ping_storage->save($ping):
  }
}

The advantages of these abstractions are that you don't have to deal with the incoming format but on the other hand it needs a bunch of code to get started.


Simply put: do not do this! Do not create your own table; that's not the way in Drupal 8. Much rather, have your own entity type. See https://drupal.stackexchange.com/a/95208/49 https://www.drupal.org/developing/api/entity https://api.drupal.org/api/drupal/core%21modules%21system%21core.api.php/group/entity_api/8 for more. The storage controller will generate the SQL schema for you and do all the storage work. The REST module will handle what you need. And so on.

Edit: For a generic case to receive and reply with JSON. First make sure there's not a Drupal API for it. Likely there is. REST gives you entities, Views gives you lists of entities (and lists of more).

If not then, you need to write a custom controller with the request as an argument. (see Parameters in routes for more) by just adding Request $request as an argument.

To respond with JSON, any Response object, among them JsonResponse. Check TimezoneController::getTimeZone and the relevant routing entry in core/modules/system/system.routing.yml.

So:

class MyController {
  public function myJson(Request $request) {
    $params = array();
    $content = $request->getContent();
    if (!empty($content)) {
      // 2nd param to get as array
      $params = json_decode($content, TRUE);
    }
    // Process $params...
    return new JsonResponse($params);
  }
}

In the controller's function how can I get the data posted with ajax and how to convert it to associative array?

use Drupal\Component\Serialization\Json;
//...
class MyController {
  public function myPostAction(Request $request) {
    $params = Json::decode($request->getContent());
    //...
  }
}

Tags:

8