Score:1

Making an HTTP subrequest causes CurrentRouteMatch to have the wrong route

by flag

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.

Jaypan avatar
de flag
Why are you doing a subrequest and not using the Guzzle HTTP client to do your request?
by flag
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.
by flag
Updated question to explain more clearly that I want to put the JSON data from one inside the other.
Score:2
cn flag

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.

by flag
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!! :)
by flag
Couldn't find an existing issue, filed https://www.drupal.org/project/drupal/issues/3218022.
Score:1
by flag

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?

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.