5

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.

3
  • Maybe you can distinguish by checking which browser they are using? See How to get exact browser name and version?
    – No Sssweat
    Jul 10, 2021 at 5:07
  • @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. Jul 10, 2021 at 6:33
  • 1
    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.
    – apaderno
    Jul 11, 2021 at 17:45

1 Answer 1

3

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.

1
  • 3
    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.
    – 4uk4
    Jul 10, 2021 at 11:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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