1

For complicated and unpleasant reasons[*], I want to embed entity data from the JSONAPI module inside JSON returned from a REST module resource.

I am trying to do this by making an HTTP subrequest to the JSONAPI module route within the REST module resource class.

Like this:

    $kernel = \Drupal::service('http_kernel');

    $current_request = \Drupal::request();

    $request = Request::create('/jsonapi/paragraph/' . $paragraph->bundle() . '/' . $paragraph->uuid->value);
    $request->setSession($current_request->getSession());

    $response = $kernel->handle($request, HttpKernelInterface::SUB_REQUEST);
    $json = $response->getContent();
    $data = json_decode($json, TRUE);

I get the data I want and it's great!

However, the main request to the REST resource endpoint crashes with this:

Symfony\Component\Serializer\Exception\NotEncodableValueException: Serialization for the format "api_json" is not supported. in Symfony\Component\Serializer\Serializer->serialize() (line 112 of /var/www/vendor/symfony/serializer/Serializer.php).

This is because in Drupal\rest\EventSubscriber\ResourceResponseSubscriber->getResponseFormat(), $route = $route_match->getRouteObject(); is the JSONAPI module route from the subrequest, and not the route from the main request.

What am I doing wrong with my subrequest?

[*] Enormous amount of custom code powering a REST resource for a decoupled front end. I want to change it to using JSONAPI but it's a massive change with huge repercussions on the frontend. To change over incrementally to JSONAPI, I want to switch some paragraph types to the JSONAPI format. Could call the JSONAPI module's PHP code directly, but that's not a public API and so future versions of Drupal could break it. Making a subrequest is using the API and so more maintainable.

3
  • Why are you doing a subrequest and not using the Guzzle HTTP client to do your request?
    – Jaypan
    Jun 7, 2021 at 15:53
  • Someone suggested I use a Symfony subrequest in Slack. Won't a Guzzle HTTP client mean an actual separate request to the server? There are LOTS of paragraph entities involved, so adding actual HTTP requests would presumably be bad for performance.
    – joachim
    Jun 7, 2021 at 15:59
  • Updated question to explain more clearly that I want to put the JSON data from one inside the other.
    – joachim
    Jun 7, 2021 at 16:00

2 Answers 2

2

After running the subrequest the request stack is quite a mess, it still contains the subrequest while the route match stack was cleaned up. If you then get the current route match this will return a newly created route match based on the json api request.

A quick fix would be to clean up the request stack by hand, including a check so that the code still works when the core issue is fixed:

$current_request = \Drupal::request();

$request = Request::create('/jsonapi/node/page/UUID', 'GET', [], $current_request->cookies->all(), [], $current_request->server->all());
$request->setSession($current_request->getSession());


$response = $kernel->handle($request, HttpKernelInterface::SUB_REQUEST);
if (\Drupal::request()->getPathInfo() !== $current_request->getPathInfo()) {
  \Drupal::requestStack()->pop();
}

BTW your subrequest probably needs more metadata from the main request. I've added cookies and server headers to the code example.

2
  • That was exactly it -- the subrequest's route match was polluting things. Is that a core bug, and is there an issue for it? I've got it all working now, thank you!! :)
    – joachim
    Jun 9, 2021 at 9:52
  • Couldn't find an existing issue, filed drupal.org/project/drupal/issues/3218022.
    – joachim
    Jun 9, 2021 at 10:08
1

I've since discovered that jsonapi_extras module provides the exact same functionality -- getting the JSONAPI representation of an entity by making a subrequest. That seems to avoid the problem by using the http_kernel.basic service to mak the subrequest. It looks like that doesn't keep a request stack, so bypasses the problem of multiple requests being kept track of?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.