5

I am getting the following when trying to redirect to an external site from my controller function defined in my routing file:

return new TrustedRedirectResponse($my_ext_url);

LogicException: The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\Core\Routing\TrustedRedirectResponse. in Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext() (line 159 of core\lib\Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber.php).

This is clearly related to cache metadata - but how do i switch this off for a trusted redirect?

Edit: There is currently no constructor on the controller and therefore no injection going on. Routing looks like (obv with correct indents :):

tocyn_payment.paypal_checkout:
path: 'test/{order_id}'
defaults:
_controller: 'Drupal\my_module\Controller\MyController::handle'
_title: 'Test Tite'
requirements:
_permission: 'access content'

Thanks in advance

Mathew

  • You need to share your code of that controller. See also drupal.org/node/2638686, you're welcome to help me convince Wim that we need change that :) – Berdir Jan 17 '16 at 20:16
  • appreciate post back. it will take me awhile to pick the bones out of that thread, but will take a look through. After seeing this drupal.org/node/2599416 am going try injecting in a helper with @url_generator.non_bubbling although without understanding at a lower level am picking a straws at mo. Also added a little more info on my controller - its literally bare bones, no construct, no injection at mo just hitting handle from routing. – Mathew Jan 17 '16 at 21:45
  • The route definition and whether you use injection or not is not really relevant. What's relevant the code in your actual method, what services/methods call and how you handle what you get back. I'll add a quick generic response. If that's not enough to resolve this, then I can be more specific if you share your code. – Berdir Jan 17 '16 at 22:07
4

You mentioned the URL generator, which is one of the two ways this happens.

In short, you need to do something like my patch in this issue: https://www.drupal.org/node/2630808

Pass TRUE to toString(), then you get a different object back that you need to add as a cacheable dependency. You can do that with such GeneratedUrl objects, entity and config objects and you should do that whenever your response depends on them.

  • My method is using services and config to generate a payment redirect for paypal adaptive pay. I went through line by line commenting out each to workout what was causing this error. I am generating route based, absolute urls to pass to paypal as part of the pay request for success and cancel. These were my issue, my helper function for these was doing : return $url->toString(); it is now $url->toString(TRUE)->getGeneratedUrl();. Error gone with no extra dependencies, nothing todo with redirect url. I do not understand what the difference between these two lines are and why the problem? – Mathew Jan 20 '16 at 11:32
  • It's because Drupal can no longer detect that you're doing something wrong and then doesn't prevent you from doing so. – Berdir Jan 20 '16 at 22:18
  • Thanks @Berdir that solved my problem ! Isn't it supposed to be a bug as related here ? drupal.stackexchange.com/questions/196520/… – pbonnefoi Sep 12 '16 at 15:13
  • Yes, it is a bug. But the difference is that here, it is your own code that has the bug. And for REST, you're just using the code like you would when clicking around in the UI and there's nothing that you can do from the outside to fix it. So the bug is in the module that provides that route (just like in this case). – Berdir Sep 12 '16 at 15:53
  • To make Matthew's comment abundantly clear, Url::toString() seems to count as a render apparently, so if you are using this in your controller anywhere and do not sent TRUE to gather the cacheable meta data you will hit this trap. Fixing my TrustedRedirectResponse url did not do the trick for me because I also had another url generation earlier in the controller. I had to adapt both. – FatherShawn Jul 16 '17 at 15:05
0

Many people may tell you this is what NOT to do, but for exotic and incomprehensible reasons, TrustedRedirectResponse can cause your controller to throw the error about metadata leaking "somewhere". I checked all around my desk and on the floor, and I couldn't find any metadata :) This can happen even if you disable caching for your route in every conceivable way.

So I thought well I'll create my trusted response but wrapping it in RenderContext bubble wrap. No dice. Maybe my situation is exotic, and the controller I'm working in does use code that's not in my control.

So if you get sick of Drupal telling you you can't do something, or want to skip hours of debugging leaky cache metadata whatever errors. Just make your own dang response..Symfony is under the hood and it's much less bossy. I added this method to my controller, and return the response, and boo-yeah, I can redirect to external sites. I just looked into the setTargetUrl() method of the RedirectResponse object.

I know that I only need to redirect to a small, finite number of external urls, so I can sanitize and white list the url input, so I only run this code to build a redirect for urls I trust.

private function createExternalRedirectResponse($url) {
    $response = new Response('', 302, []);
    $response->targetUrl = $url;
    $response->setContent(sprintf('<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="refresh" content="0;url=%1$s" />

        <title>Redirecting to %1$s</title>
    </head>
    <body>
        Redirecting to <a href="%1$s">%1$s</a>.
    </body>
</html>', htmlspecialchars($url, ENT_QUOTES, 'UTF-8')));
    $response->headers->set('Location', $url);
    return $response;
  }

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.