1

I have a custom route on my site at /search. This page has a couple of forms on it (defined with FormBase).

When I am logged in, the form appears to work.

When I am anonymous, many many requests occur when I submit and causes a "too many redirects" error in the browser.

Here is my routing.yml

mymodule.search:
 path: '/search'
 defaults:
   _title_callback: 'Drupal\mymodule\Controller\SearchPage::title'
   _controller: 'Drupal\mymodule\Controller\SearchPage::build'
 requirements:
   _permission: 'access content'

Here is one of the forms:

<?php

namespace Drupal\mymodule\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Drupal\Component\Utility\Xss;
use Drupal\Component\Utility\Unicode;

/**
 * Class KeywordForm.
 *
 * @package Drupal\mymodule\Form
 */
class KeywordForm extends FormBase {

  /**
   * Symfony\Component\HttpFoundation\RequestStack definition.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  protected $requestStack;

  /**
   * Constructor for our class.
   */
  public function __construct(RequestStack $request_stack) {
    $this->requestStack = $request_stack;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('request_stack')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'iana_keyword_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $keywords = Xss::filter($this->requestStack->getCurrentRequest()->get('keywords'));
    $site_ids = $this->requestStack->getCurrentRequest()->get('site_id');
    $sort_by = $this->requestStack->getCurrentRequest()->get('sort_by');
    $sort_order = $this->requestStack->getCurrentRequest()->get('sort_order');

    $form['#attributes']['class'][] = 'search-result__form';

    if (!is_array($site_ids)) {
      $site_ids = [];
    }

    $form['keywords'] = [
      '#type' => 'textfield',
      '#cache' => ['contexts' => ['url.query_args:keywords']],
      '#default_value' => Unicode::strlen($keywords) ? $keywords : '',
      '#prefix' => '<div class="search-box__input js-search-input">',
      '#suffix' => '</div>',
      '#attributes' => [
        'placeholder' => Unicode::strlen($keywords) ? $keywords : t('Search for...'),
      ],
    ];

    $form['site_id'] = [
      '#type' => 'hidden',
      '#value' => $site_ids,
    ];

    $form['sort_by'] = [
      '#type' => 'hidden',
      '#value' => $sort_by,
    ];

    $form['sort_order'] = [
      '#type' => 'hidden',
      '#value' => $sort_order,
    ];

    $form['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Search'),
      '#attributes' => [
        'class' => [
          'search-box__button',
        ],
        'data-twig-suggestion' => 'search_results_submit',
      ],
    ];

    $form['#cache']['contexts'][] = 'session';
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $form_state->setRedirect(
      'mymodule.search',
      [
        'keywords' => $form_state->getValue('keywords'),
        'site_id' => $form_state->getValue('site_id'),
        'sort_by' => $form_state->getValue('sort_by'),
        'sort_order' => $form_state->getValue('sort_order'),
      ],
      [
        'query' => [
          'keywords' => $form_state->getValue('keywords'),
          'site_id' => $form_state->getValue('site_id'),
          'sort_by' => $form_state->getValue('sort_by'),
          'sort_order' => $form_state->getValue('sort_order'),
        ],
      ]
    );
  }

}

It looks like at some point, I hit this bit of code in the FormBuilder class:

// If the form returns a response, skip subsequent page construction by
// throwing an exception.
// @see Drupal\Core\EventSubscriber\EnforcedFormResponseSubscriber
//
// @todo Exceptions should not be used for code flow control. However, the
//   Form API does not integrate with the HTTP Kernel based architecture of
//   Drupal 8. In order to resolve this issue properly it is necessary to
//   completely separate form submission from rendering.
//   @see https://www.drupal.org/node/2367555
if ($response instanceof Response) {
  throw new EnforcedResponseException($response);
}

Here is what Google Chrome devtools window shows for network activity:

enter image description here

What is going on here, and how can I fix this? I need the values attached to the URL when the form is submitted.

All of the Forms are displayed on the sidebar and header as blocks injecting those forms. Only 3 forms exist, keyword and a simple checkbox filter.

For the life of me, I can't see what is wrong.

The cache_render table is filling up with various search URLs, it should not, though no matter what I set #cache to, it does.

  • Do you have the module Redirect installed? – 4k4 Jun 21 '17 at 6:42
  • Yes, I do. I looked to see if there were redirects from search, I did not see any. – Kevin Jun 21 '17 at 10:50
  • No, this is about the global redirect settings, disable all of the seven options and test again, there is an issue concerning anonymous traffic. – 4k4 Jun 21 '17 at 11:52
  • I will try that. Is the use of setRedirect in this manner now discouraged? It used to work in D7. – Kevin Jun 21 '17 at 11:56
  • setRedirect is not the problem, but you can get a problem how the Redirect module handles the resulting redirect response. – 4k4 Jun 21 '17 at 12:01
2

Thanks to 4k4 and some folks in IRC, it turns out that the culprit here is the Redirect module.

I was able to solve this by:

  • Disable Enforce clean and canonical URLs. in Redirect
  • Add /search and /search* to URLs to ignore in Redirect

After setting that and clearing cache, anonymous users were no longer stuck in an endless redirect loop when using search forms.

  • You shouldn't have to do this. that you add your route with both route parameters and query arguments for the same is weird and shouldhn't be necessary. Not sure how you ignored the URL's, but the ignore in the settings is just about 404 logging and has nothing to do with redirects. If there really is no other way, you could opt out of the clean feature by setting an option on your route, check the redirect request subscriber. But that should only be necessary in case of weird request processing like private files – Berdir Jul 3 '17 at 20:34

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.