Почему это вызывает бесконечный цикл запроса? - PullRequest
5 голосов
/ 07 апреля 2011

Ранее сегодня я помогал кому-то с .htaccess вариантом использования, и придумал решение , которое работает, но сам не могу понять это!

Он хотелчтобы иметь возможность:

  • Просмотреть index.php?id=3&cat=5
  • См. строку адреса, читать index/3/5/
  • Подача контента с index.php?id=3&cat=5

Последние два шага довольно типичны (обычно от пользователя, вводящего index/3/5 в первую очередь), но первый шаг был необходим, потому что у него все еще были ссылки в старом формате на своем сайте и, для чего бы то ни былопричина, не мог изменить их.Поэтому ему нужно было поддерживать оба формата URL, и чтобы пользователь всегда в конечном итоге видел предварительно подтвержденный.

После долгих попыток мы получили следующее .htaccess file:

RewriteEngine on

# Prevents browser looping, which does seem
#   to occur in some specific scenarios. Can't
#   explain the mechanics of this problem in
#   detail, but there we go.
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule .* - [L]

# Hard-rewrite ("[R]") to "friendly" URL.
# Needs RewriteCond to match original querystring.
# Uses "?" in target to remove original querystring,
#   and "%n" backrefs to move its components.
# Target must be a full path as it's a hard-rewrite.
RewriteCond %{QUERY_STRING} ^id=(\d+)&cat=(\d+)$
RewriteRule ^index\.php$ http://example.com/index/%1/%2/? [L,R]

# Soft-rewrite from "friendly" URL to "real" URL.
# Transparent to browser.
RewriteRule ^index/(\d+)/(\d+)/$ /index.php?id=$1&cat=$2

Хотя это может показаться странным вариантом использования (" почему бы просто не использовать правильные ссылки в первую очередь? ", спросите вы)Просто иди с ним.Независимо от первоначального требования, это сценарий, и он сводит меня с ума.

Без первого правила клиент входит в цикл запроса, пытаясь повторно GET /index/X/Y/ и каждый раз получать 302.Проверка на REDIRECT_STATUS заставляет все работать гладко.Но я бы подумал, что после окончательного правила больше правила не будут выполняться, клиент больше не будет делать запросов (примечание, нет [R]), и все будет подливой.

Итак... почему это приводит к циклу запроса, когда я вынимаю первое правило?

Ответы [ 2 ]

4 голосов
/ 07 апреля 2011

Не имея возможности возиться с вашей настройкой, я не могу сказать наверняка, но я полагаю, что эта проблема связана со следующей относительно загадочной функцией mod_rewrite:

Когда вы манипулируете URL / именем файла в контексте каждого каталога, mod_rewrite сначала перезаписывает имя файла обратно в соответствующий ему URL (что обычно невозможно, но см. Ниже директиву RewriteBase, чтобы добиться этого), а затем инициирует новый внутренний подзапрос с новым URL. Это возобновляет обработку фаз API.

(источник: mod_rewrite, техническая документация , я очень рекомендую прочитать)

Другими словами, когда вы используете RewriteRule в файле .htaccess, возможно, что новый переписанный URL-адрес сопоставляется с совершенно другим каталогом в файловой системе, в этом случае файл .htaccess в исходном файле Каталог не будет применяться больше. Поэтому, когда запрос RewriteRule в файле .htaccess соответствует запросу, Apache должен возобновить обработку с нуля с измененным URL-адресом. Это означает, среди прочего, что каждый RewriteRule проверяется снова.

В вашем случае вы получаете доступ к /index/X/Y/ из браузера. Последнее правило в вашем файле .htaccess сработает, переписав его в /index.php?id=X&cat=Y, поэтому Apache должен создать новый внутренний подзапрос с URL /index.php?id=X&cat=Y. Это соответствует вашему ранее правилу внешнего перенаправления, поэтому Apache отправляет ответ 302 обратно в браузер, чтобы перенаправить его на /index/X/Y/. Но помните, браузер никогда не видел этот внутренний подзапрос; насколько он знает, это было уже на /index/X/Y/. Так что вам кажется, что вы перенаправлены с /index/X/Y/ на тот же URL, вызывая бесконечный цикл.

Помимо снижения производительности, это, вероятно, одна из лучших причин, по которой следует избегать добавления правил перезаписи в файлы .htaccess, когда это возможно. Если вы перенесете эти правила в конфигурацию основного сервера, у вас не возникнет этой проблемы, поскольку совпадения в правилах не вызовут внутренние подзапросы. Если у вас нет доступа к основным файлам конфигурации сервера, один из способов обойти это ( EDIT : или так я думал, хотя это не работает - см. Комментарии), добавив флаг [NS] (без подзапроса) для правила внешнего перенаправления,

RewriteRule ^index\.php$ http://example.com/index/%1/%2/? [L,R,NS]

Как только вы это сделаете, вам больше не нужно первое правило, проверяющее REDIRECT_STATUS.

0 голосов
/ 08 января 2012

Решение, приведенное ниже, сработало для меня.

RewriteEngine on
RewriteBase /

#rule1
#Guard condition: only if the original client request was for index.php
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /index\.php [NC]
RewriteCond %{QUERY_STRING} ^id=(\d+)&cat=(\d+)$ [NC]
RewriteRule . /index/%1/%2/? [L,R]

#rule 2
RewriteRule ^index/(\d+)/(\d+)/$ /index.php?id=$1&cat=$2 [L,NC]

Вот то, что я думаю, происходит

Из приведенных выше шагов

  1. Перейдите к index.php? Id = 3 & cat = 5
  2. См. Строку адреса, прочитайте index / 3/5 /
  3. Получите контент из index.php? Id = 3 & cat =5

На шаге 1 правило 1 сопоставляется и перенаправляет на адресную строку и выполняет шаг 2.

На шаге 3 правило 2 теперь соответствует и переписывается в index.php.

Правила перезапускаются по причинам, указанным Дэвидом, но, поскольку THE_REQUEST является неизменяемым после установки исходного запроса, он все равно содержит /index/3/5, поэтому правило 1 не соответствует.

Правило 2тоже не совпадает, и результат index.php обслуживается.

Большинство других переменных являются изменяемыми, например REQUEST_URI.Их модификация во время обработки правил и неправильное ожидание того, что сопоставления с шаблоном совпадают с исходным запросом, является обычной причиной для бесконечных циклов.

Иногда это кажется довольно эзотерическим, но я уверен, что есть логическая причина для этогосложность: -)

РЕДАКТИРОВАТЬ

Конечно, есть два разных запроса

Есть 2 клиентских запроса, исходныйиз шага 1 и из внешнего перенаправления в шаге 2.

То, что я упомянул выше, это то, что когда правило 2 совпадает со вторым запросом, оно переписывается в /index.php и вызывает внутреннее перенаправление.Это заставляет файл .htaccess для каталога / снова загружаться (это может быть просто другой каталог с другими правилами .htaccess) и снова запускать все правила.

Итак... почему это привело бы к циклу запроса, когда я извлекаю первое правило?

Когда правила запускаются повторно, первое правило теперь неожиданно совпадает, в результате перезаписи Rule2, иделает перенаправление, вызывая бесконечный цикл.

Ответ Дэвида содержит большую часть этой информации, и именно это я имел в виду «по причинам, указанным Дэвидом».

Однако главное здесь заключается в том, что вам действительно нужно дополнительное условие, либо вашеусловие, которое останавливает дальнейшую обработку правила для внутренних перенаправлений, или мое, которое препятствует сопоставлению правила 1, необходимо для предотвращения бесконечного цикла.

...