Проблема кнопки "Back" и проблема кнопки "Refresh"
Филипп Зуев, Ирина Пономарева, Владимир Якименко
Во всех современных браузерах есть кнопка "Back", которая позволяет пользователю повторно просмотреть те страницы, которые он уже видел раньше.
Также существует кнопка "Refresh", которая позволяет "обновить" страницу, которую пользователь просматривает.
Эти две кнопки предназначены для удобства пользователя, но иногда после нажатия на одну из них пользователь вместо страницы, которую он ожидал, видит совсем другую.
Эта статья была написана для того, чтобы объяснить, почему это происходит и как этого избежать.
Страницы бывают двух видов: статические и динамические (последние еще иногда называют скриптами). Статическая страница всегда выглядит для пользователя одинаково. Динамическая страница, как правило, выглядит по-разному в зависимости от того, какие данные были переданы от браузера серверу. Мы поговорим о том, как эти данные передаются, когда будем рассказывать об отличии методов GET и POST.
Каждый раз, когда браузер запрашивает с сервера новую страницу, он передает серверу информацию, которая необходима, чтобы найти или создать эту страницу. Эта информация обязательно содержит:
адрес страницы (который ещё называют Universal Resource Locator - URL),
зарезервированное слово HTTP-протокола (GET или POST), которое сообщает серверу, что именно пользователь браузера хочет сделать со страницей. Дело в том, что некоторые браузеры позволяют своим пользователям не только просматривать страницы с сервера, но и заменять их другими страницами по своему усмотрению или даже удалять. Разумеется, сервер не каждому пользователю позволит изменять или удалять страницы. Это зарезервированное слово называется "метод" или "метод протокола http". Методы GET и POST используются для получения страницы с сервера, при этом они могут посылать серверу дополнительные данные, которые сервер может использовать для формирования страницы. Метод GET показывает эти данные в адресной строке браузера. Они выглядят как символ "?" с какой - либо строчкой после него. Метод POST этого не делает, посылая серверу данные немного другим образом. Есть и ещё одно отличие, о нем ниже.
Браузер показывает пользователю страницу и ещё запоминает её, чтобы в следующий раз не повторять весь ресурсоемкий процесс общения с сервером заново. Это называется "кешированием" страницы (от англ. слова cache, означающего память, в которой программы, например, браузеры, хранят информацию для повторного применения). Впрочем, сервер может вместе со страницей передать браузеру "просьбу" - не кешировать. Ответы от сервера, полученные методом POST, не кешируются, если сервер явно не "попросил" браузер кешировать. То же самое относится к страницам с адресом, который содержит символ "?" с параметрами после него. Ответы, полученные методом GET, напротив, кешируются по умолчанию.
Кеширование повышает скорость, с которой браузер показывает страницы. Почему же тогда сервер может попросить браузер не кешировать страницы? Представьте себе страницу, которая, например, показывает текущее время. Если браузер покажет эту страницу, взяв ее из кеша, то, разумеется, время на странице будет показано неправильно.
Существует индустриальный стандарт - rfc 2616 ftp://ftp.rfc-editor.org/in-notes/rfc2616.txt, который определяет, как производители браузеров должны реализовывать функциональность, в том числе и функциональность кнопки "Back". Функциональность кнопки "Refresh" не очерчена в спецификации прямо, но к ней имеют отношение разделы, посвященные кешированию (Параграфы 13.1 – 13.12 ).
При нажатии кнопки "Back" браузер должен показывать в точности ту страницу, которую пользователь уже просматривал ранее (rfc 2616, параграф 13.13) . Согласно спецификации, браузер в ответ на нажатие кнопки "Back" не должен делать запрос к скрипту, а вместо этого должен показать пользователю страницу, которую браузер "запомнил" в кеш-памяти. В таких случаях, страница, которую пользователь увидел по нажатию кнопки "Back", будет не такой, какую ему выдал бы скрипт с сервера. Это означает, что она будет содержать устаревшую информацию, которая может ввести пользователя в заблуждение.
В ситуации, описанной выше, согласно rfc 2616, браузер должен предупредить пользователя. Internet Explorer 6 показывает в таких случаях следующий текст:
Иногда автор веб приложения предпочитает не смущать пользователей этим предупреждением.
В таких случаях автору веб приложения надо заставить браузер либо не показывать предупреждение и просто заново запрашивать страницу с сервера (если страница действительно будет содержать более новую информацию), либо заставить браузер просто брать страницу из кеш-памяти.
К сожалению, спецификация не определяет никакого способа, которым это можно было бы сделать.
Есть еще одна возможная проблема с кнопкой "Back". Пользователю будет показана устаревшая версия страницы, и, если браузер не станет предупреждать об этом, пользователь может быть введен в заблуждение.
Третья возможная проблема – браузер запросит страницу с сервера заново. Либо он перед этим покажет пользователю предупреждение "Внимание: страница устарела" или даже не будет его показывать, а просто сделает запрос.
Тогда серверное приложение получит два запроса с одинаковыми параметрами. Это может нарушить работу приложения. Например, оно повторно отправит письмо, если это приложение - почтовая система. Если повторно запрошенная страница – это страница, на которой пользователь вводит своё имя и пароль , чтобы зарегистрироваться в системе, тогда приложение фактически получит просьбу ещё раз авторизовать уже авторизованного пользователя. Это в большинстве случаев означает, что пользователю будет выдана страница с сообщением об ошибке.
Вторая и третья из перечисленных проблем в той же мере относятся и к кнопке "Refresh".
Решения проблем связанных с кнопкой "Back" отличаются от решений, которые применимы к "Refresh" из- за следующего обстоятельства. В пункте 13.13 RFC 2616 определено, что браузер не должен учитывать "просьбы" сервера не кешировать страницы, когда он показывает их в ответ на нажатие кнопки "Back". Во всех остальных случая, например, при нажатии пользователем кнопки "Refresh" браузер обязан учитывать эти "просьбы".
Поэтому проблемы, вызванные тем, что браузер после нажатия кнопки "Refresh" показывает пользователю устаревшую версию страницы, которую он взял из кеша, можно решить простым запретом кеширования.
Варианты решения проблем, связанных с кнопкой "Back":
1. Можно просто запретить кнопку "Back", точнее, сделать так, что браузер будет игнорировать любые нажатия на неё. Для этого надо все ссылки на странице написать не так: <A href="http://www.servername.com/index.php">текст ссылки</A>, а так:<A href="javascript:window.location.replace(‘http://www.servername.com/index.php’)">текст ссылки</A> Недостаток – если пользователь запретил своему браузеру выполнять javascript, тогда ссылки вида <A href=”javascript:…” > перестанут работать.
2. Чтобы решить проблему кнопки "Back", можно заменить те ссылки, которые содержат в себе параметры (например, “http://www.servername.com/cgi-bin/script.cgi?parameter1=1¶meter2=2”) на ссылки, которые выглядят так - "http://www.servername.com/cgi-bin/script_parameter1_1_parameter2_2.html". Каждая такая ссылка выглядит для браузера как ссылка на отдельный html файл, в нашем примере этот файл называется script_parameter1_1_parameter2_2.html. Сервер сможет извлечь из имени файла всю необходимую информацию, которую он раньше извлекал из параметров. При этом способе требуется настройка веб сервера. Проще всего это сделать с помощью страницы – обработчика ошибки 404 (page not found). Если ваш сервер – это apache (www.apache.org) то документацию по его настройке можно прочитать здесь.
Недостаток – ваш хостинг провайдер может не позволить вам настраивать его сервер по вашему вкусу.
Преимущество – работает в любых браузерах с любыми настройками.
3. При этом способе сервер иначе обрабатывает запросы. Вместо того, чтобы выдать браузеру страницу, сервер посылает ему ответ, описанный в спецификации http (rfc 2616 параграф 10.3.4) смысл которого в том, что запрошенная браузером страница находится по другому адресу, и браузер должен перенаправить свой запрос на этот адрес.
В такой ситуации большинство браузеров не сочтут полученную ими в конечном счете страницу сформированной скриптом.
Например, сервер в ответ на запрос пошлет браузеру такой http ответ:
HTTP/1.1 303 See Other Date: Tue, 10 Jun 2003 11:41:54 GMT Server: Apache/1.3.27 (Red Hat Linux) Location: /new_location_for_page.html
Браузер тогда запросит с сервера страницу /new_location_for_page.html и покажет её пользователю. Он не будет считать её страницей, сформированной скриптом и, соответственно, не будет показывать пользователю предупреждений, когда тот нажмет "Back", чтобы снова посмотреть её.
Проблемы с кнопкой "Refresh" решаются проще, чем с кнопкой "Back", благодаря тому, что в случае с кнопкой "Refresh" браузер обязан учитывать HTTP-заголовки, посылаемые ему сервером, с помощью которых сервер может изменять режим кеширования, который использует браузер.
Пользователи веб-приложений чаще всего сталкиваются с проблемой, связанной с кнопкой "Refresh", следующим образом: пользователь нажимает "Refresh", чтобы обновить страницу, но к его удивлению, браузер показывает ему совсем другую страницу с текстом наподобие следующего: “Внимание, страница устарела”. Это происходит из-за того, что авторы веб-приложения запретили браузеру кешировать эту страницу. В некоторых случаях это необходимо, но чаще всего этого можно избежать. Кеширование приходится запрещать, если веб-приложение использует cookies для реализации сессий. Веб-приложению для того, чтобы узнать, изменились ли данные, которые надо показывать пользователю на странице, почти всегда требуется его сначала авторизовать, для чего используется информация из cookies. Если браузер кеширует страницу, он не будет передавать cookie серверу. В таких случаях рекомендуется запрещать кеширование не для всей страницы, а только для одного заголовка HTTP-пакета, который и содержит в себе cookie.
Для этого ответ от сервера должен содержать следующий HTTP-заголовок:
Этот метод работает только с браузерами, поддерживающими спецификацию 1.1. Впрочем, к таковым относятся браузеры Internet Explorer и Netscape Navigator, начиная с 4-х версий.
Вторая распространенная проблема с кнопкой "Refresh" следующая:
Нажав на кнопку "Refresh" пользователь заставляет свой браузер повторно послать точно такой же запрос, который был послан ранее. Это может нарушить работу вашего приложения. Например, ваше приложение попытается вставить такие же данные в базу данных или повторно послать такое же письмо. Чтобы избежать этого, автору приложения придется переделать его следующим образом: в каждом запросе, который браузер отправляет серверу, должен быть случайный параметр, который различается в каждом запросе от браузера и формируется, например, при помощи javascript.
Каждый раз, обработав запрос, серверное приложение должно запомнить, используя для этого, например, сессии, значение этого случайного параметра, поступившего в запросе.
Если после этого пользователь нажмет кнопку "Refresh", сервер получит два полностью идентичных запроса. Поскольку серверное приложение знает, что каждый запрос должен отличаться от предыдущего как минимум одним случайным параметром, второй запрос в такой ситуации он может проигнорировать.
Проблемы, связанные с использованием кнопок "Back" и "Refresh", часто ставят в тупик как пользователей сети Internet, впервые столкнувшихся с необъяснимым поведением используемого ими веб-приложения, так и программистов, сравнительно недавно начавших самостоятельно разрабатывать такие приложения. Авторы статьи надеются, что освещенные здесь вкратце причины возникающих проблем и варианты их решений помогут и тем, и другим избежать недоуменных вопросов и сэкономить время, которое в противном случае было бы зря потрачено на изобретение решений, которые уже не раз изобретались другими людьми.