![]() |
![]() |
|
||
![]() |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| [28 февраля 2002 г.] |
Сегодня мы поговорим о такой важной вещи, как переадресация. Это слово, как и множество других компьютерных терминов, необходимо понимать «объемно», а не «поверхностно». То есть, когда речь заходит о переадресации, не нужно лихорадочно представлять себе почтальона с ноутбуком на багажнике велосипеда (не
В чем же смысл редиректа?.. Он прост.
Я очень старался подобрать точное определение, но в конце все-таки решил остановиться на более коротком и сделать пару оговорок. Во-первых, под «скриптом» здесь понимается все, что угодно, а не обязательно программный код: например, переадресация вполне может происходить с одной «статической» страницы на другую. Во-вторых, под «другим скриптом» в действительности надо понимать «другой процесс», потому что скрипт может быть и тем же самым (как при самопереадресации). |
Казалось бы, все просто. Оказывается, не совсем. Но, прежде чем это обсуждать, давайте рассмотрим главный способ
Самый простой и универсальный способ выполнить внешний
<?
# 0 означает, что переадресация произойдет
# через 0 секунд, то есть, немедленно.
print '
<meta http-equiv=Refresh
content="0; URL=/some/other/script.html">
';
exit();
?>
Это еще иногда называется «перещелкиванием» из-за характерного звука, издаваемого браузером при приеме такого тэга. Второй способ
<?
Header("Location: http://$SERVER_NAME/some/other/script.html");
exit();
?>
Обратите внимание на то, что во втором случае я использовал полный URL, а не один лишь URI, как в предыдущем примере. Сейчас будет ясно, зачем так сделано.
Я только что проверил, и, похоже, при использовании заголовка |
Кроме мерзкого щелчка, внешний редирект имеет еще один недостаток: задержку из-за необходимости по модему передавать туда-сюда данные запроса-ответа. Иными словами, он не проходит незамеченным для браузера. Это означает, что, получив команду на внешний редирект, браузер полностью забывает о предыдущем скрипте и всецело отдает себя служению порожденному запросу. Новый адрес даже появляется в адресной строке окна. Хорошо это или плохо, мы посмотрим чуть позже, а пока посмотрим, как внутренний редирект решает вопрос (вернее, пытается решить).
В отличие от внешнего, внутренний редирект обрабатывается на сервере, а не в браузере. Это значит, что браузер может и не догадываться, что скрипт совершил внутреннюю переадресацию: для него это будет та же самая страница. Иными словами, при выполнении внутреннего редиректа браузер продолжает «думать», что страницу вернул ему запущенный скрипт, хотя на самом деле это может быть не так.
При работе с Apache, насколько я знаю, существует лишь один способ выполнить внутренний редирект: указать в заголовке
<?
Header("Location: /some/other/script.html");
exit();
?>
Когда сервер получает от скрипта страницу и собирается отправить ее браузеру, он прежде всего проверяет: нет ли в ней заголовка
Теперь настала пора оценить эти два вида редиректов и выбрать лучший. Все зависит от того, по какой архитектуре построен ваш сайт. Но прежде вам необходимо прочитать шестнадцатую наблу, потому что именно там обсуждаются вопросы архитектуры, без которых мы не сможем двинуться дальше.
Итак, вы вернулись. Хороший знак. Продолжим. Если вы используете «способ третий», описанный в шестнадцатой набле (например, боготворите компанию Sun и пишете сервлеты и JSP-страницы), внутренний редирект будет для вас частым гостем. Честно говоря, в спецификации JSP вообще заявляют, что это единственно удобный способ для больших Web-приложений. Там предлагают примерно такой способ:
Вообще-то, в книгах принято говорить не «сессия», а «сеанс», но мне последний термин не нравится. Во-первых, он очень напоминает фразу «сеанс у психиатра» или «сеанс одновременной игры». А во-вторых, на английский сеанс переводится как «session», и транслитерация тут вполне уместна. Грубо говоря, |
Я здесь говорю о JSP и Sun потому, что в их примере внутренний редирект действительно работает так, как они этого и хотели. Итак, здесь переадресация используется для передачи управления шаблону.
Давайте теперь рассмотрим недостаток внутреннего редиректа. Он всего один: неведение браузера относительно той катавасии, которая в действительности происходит на сервере. Пусть, к примеру, скрипт, выполняющий переадресацию, располагается по адресу
Что же получается?.. Если неавторизованный пользователь запустит скрипт, произойдет внутренняя переадресация на страницу регистрации, однако URL в адресной строке останется
Похожий пример уже рассматривался в шестнадцатой набле, но он не идентичен примеру выше. Если пользователь зайдет на страницу регистрации «вручную» (набрав ее адрес в браузере), то картинка окажется на |
Все это особенно задевает за живое, когда используется «дедовское CGI-программирование» с использованием директории
Решение проблемы (вернее, затычка для дыры) всегда использовать для изображений абсолютный путь (например,
Рекомендую избегать абсолютных путей, но в то же время не использовать и пути вида |
Наверное, вы поняли, что я хочу сказать всеми этими примерами. Внутренний редирект имеет принципиальные уязвимости, даже при использовании «способа третьего» из шестнадцатой наблы. Что уж говорить о «четвертом способе» и «
Вообще, считается, что пользователь любит, когда к нему относятся по-человечески. При этом он быстро привыкает к хорошему. Вы можете в этом убедиться, написав программу, которая на время своей работы превращает курсор мыши в песочные часы. Запустите ее и понаблюдайте за собой: вы заметите странную вещь. Вы начнете испытывать какое-то внутреннее напряжение, увидя «фальшивые» песочные часы. Ха-ха, даже если вы сами писали эту программу, вы все равно будете чего-то ждать! Но вы ведь прекрасно понимаете, что система вовсе не занята обработкой данных, а просто издевается над вами. Вот что |
Про внутренний и внешний редиректы сказано достаточно, перейдем теперь к так называемому self-редиректу (самопереадресации), когда страница заставляет браузер перейти... на саму себя. Звучит довольно загадочно: зачем это вообще может понадобиться?.. Отвечаю: из-за недосмотра разработчиков протокола HTTP (см. об этом в конце наблы).
Проще всего опять рассмотреть пример. Пусть у нас есть небольшая гостевая книга
<?
$FNAME = "book.txt";
if(@$doAdd) {
$f=fopen($FNAME,"a");
if(@$text) fputs($f,$text."\n");
fclose($f);
}
$GB=@file($FNAME);
if(!$GB) $GB=array();
?>
<form action=<?=$SCRIPT_NAME?> method=POST>
Текст:<br>
<textarea name=text></textarea><br>
<input type=submit name=doAdd value="Добавить">
</form>
<?foreach($GB as $text) {?>
<?=$text?><br><hr>
<?}?>
Такой метод передачи данных «самому себе» прекрасно себя зарекомендовал в Web-программировании и полностью соответствует идеологии «способа четвертого» из шестнадцатой наблы. Он имеет множество достоинств, например, отсутствие «некорректных» ссылок и ясность для пользователя. Корпорация Sun, как обычно, выступает против, но это уже совсем другая история. Для сокращения письма я не использую в приведенном выше примере отдельный шаблон, а просто «прилепляю» шаблон в конец скрипта. |
Пусть теперь пользователь ввел в браузере адрес bad.php, напечатал текст и нажал на кнопку добавления. Щелкните по ссылке и проделайте это, а затем, когда сообщение будет добавлено, нажмите кнопку Обновить в браузере. Перед вами предстанет диалог с вопросом: точно ли вы уверены, что хотите отправить форму снова?.. Ну
Обратите внимание, что self-редирект не бывает |
Вот как будет выглядеть корректно работающий скрипт:
<?
$FNAME = "book.txt";
if(@$doAdd) {
$f=fopen($FNAME,"a");
if(@$text) fputs($f,$text."\n");
fclose($f);
$rnd=time(); # ВНИМАНИЕ!
Header("Location: http://$SERVER_NAME$SCRIPT_NAME?$rnd");
exit;
}
$GB=@file($FNAME);
if(!$GB) $GB=array();
?>
<form action=<?=$SCRIPT_NAME?> method=POST>
Текст:<br>
<textarea name=text></textarea><br>
<input type=submit name=doAdd value="Добавить">
</form>
<?foreach($GB as $text) {?>
<?=$text?><br><hr>
<?}?>
Настоятельно рекомендую вам обратить внимание на строчку, помеченную комментарием «ВНИМАНИЕ!». Вы можете видеть, что я «прицепляю» в хвост URL текущее время в секундах, выступающее здесь в качестве уникального идентификатора. Такой маневр сильно уродует URL в адресной строке, однако он действительно необходим из-за ошибки в одной из версий браузера Netscape. А именно, при самопереадресации (и только при этом виде редиректа, когда URL остается неизменным) Netscape иногда вдруг выдает вместо страницы сообщение «Документ не содержит данных». Браузеру все «до лампочки», что видно хотя бы по лампочкам приема у модема: страницу он скачал, но не отобразил. Мистика, на выяснение обстоятельств которой я однажды потратил целый день.
|
Добавьте сообщение, а затем нажмите Обновить в
На этой оптимистической ноте позвольте закончить наблу. Хотя нет, давайте помечтаем о том времени, когда разработчики протокола HTTP догадаются ввести специальный заголовок, который будет действовать на кнопку Обновить точно так же, как и самопереадресация. А именно, в присутствии этого заголовка браузер вместо POST-запроса обновления должен посылать запрос GET по тому же самому URL. Конечно, только в том случае, если предыдущий POST-запрос сработал корректно (то есть, например, не произошло разрыва связи). Звучит заманчиво, не правда ли?..
![]() |
| ||||||||||||||||||||||||||||
| Дмитрий Котеров | 28 февраля 2002 г. ©1999-2010 | | Контакт | Вернуться к оглавлению |