Score:5

In a hook, how can I distinguish between web users and JSON:API users?

cn flag

I have a Drupal website that also hosts an Ionic app via JSON:API. I want website users to be redirected on entity insert, so I added a redirect like this:

function MYMODULE_flagging_insert(FlaggingInterface $flagging) {
  $redirect_url = Url::fromRoute(MYCLASS::SECRET_ROUTE)->toString();
  $response = new RedirectResponse($url);
  $response->send();
}

This code will redirect web users as expected when they add a flag. However, this code breaks the JSON:API app. When I add a flag with a user from JSON:API, I get this error:

    RuntimeException: Failed to start the session because headers have already been sent by "/app/vendor/symfony/http-foundation/Response.php" at line 384. in Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage->start() (line 152 of /app/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php)
#0 /app/web/core/lib/Drupal/Core/Session/SessionManager.php(162): Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage->start()
#1 /app/web/core/lib/Drupal/Core/Session/SessionManager.php(193): Drupal\Core\Session\SessionManager->startNow()
#2 /app/vendor/symfony/http-foundation/Session/Session.php(189): Drupal\Core\Session\SessionManager->save()
#3 /app/web/core/lib/Drupal/Core/StackMiddleware/Session.php(60): Symfony\Component\HttpFoundation\Session\Session->save()
#4 /app/web/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\Core\StackMiddleware\Session->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#5 /app/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(106): Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#6 /app/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(85): Drupal\page_cache\StackMiddleware\PageCache->pass(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#7 /app/vendor/asm89/stack-cors/src/Asm89/Stack/Cors.php(60): Drupal\page_cache\StackMiddleware\PageCache->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#8 /app/web/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Asm89\Stack\Cors->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#9 /app/web/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(52): Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#10 /app/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#11 /app/web/core/lib/Drupal/Core/DrupalKernel.php(716): Stack\StackedHttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#12 /app/web/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request))
#13 {main}

So is there a way to detect in an insert hook (or other hooks) whether the user is logged in through the Drupal website or through JSON:API?

I want to add a redirect if the user is accessing Drupal through the website, but I don't want to use a redirect if the user is accessing Drupal through the app.

No Sssweat avatar
ua flag
Maybe you can distinguish by checking which browser they are using? See [How to get exact browser name and version?](https://stackoverflow.com/questions/8754080/how-to-get-exact-browser-name-and-version/8754134)
cn flag
@NoSssweat I'm using the Ionic framework for my app, which displays content in a webview. So the browser string will be the same for mobile app users and users accessing the website through their mobile devices.
apaderno avatar
us flag
`hook_entity_insert()` is the wrong hook to redirect users. It assumes the users are creating an entity from the UI, but that's not always the case, as the hook would be invoked also when the entity is programmatically created. Similar mistake is redirecting users in `hook_entity_view()`, which is also invoked when cron tasks run: It's the hook used from the Search module to index nodes.
Score:3
cn flag

Using redirects with $response->send() like this used to work:

  $response = new RedirectResponse($url);
  $response->send();

But, this no longer works in Drupal 9.2. Also, it breaks JSON:API in versions before 9.2. The correct way to do it is described in detail in this excellent answer.

Basically, you need to create your own custom http_middleware to handle the redirects. Since http_middleware only applies to http/web requests, it doesn't interfere with JSON:API, so the problem is solved.

4uk4 avatar
cn flag
This approach might work in this specific POST request, but in a normal Drupal GET request I would use a cacheable TrustedRedirectResponse as soon as possible. For example if it depends only on the authenticated user in a request event subscriber right after the authentication has happened. Or in an exception or response event subscriber if you need to wait until an exception is raised or a response is built.
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.