URL rewriting
Проблемы
Существует несколько причин, по которым ни одна внутренняя ссылка в сложном WEB-приложении не должна закладываться напрямую в HTML-шаблон. Все гиперссылки на вашей странице должны проходить унифицированную обработку.
Прежде всего, это необходимо для борьбы с браузерным кэшем. Вы можете выдавать какие угодно HTTP-заголовки по поводу даты последнего изменения страницы, но тонкий клиент всё равно будет считать вас дурачком и вместо запроса на обновлённую форму ввода или js-библиотеку доставать старую версию из буфера. Есть только один способ предотвратить это: приписывать случайный параметр к каждой строке запроса.
Кроме того, если только вы не заботитесь об эстетике URL (а в информационных intranet-системах это совершенно бессмысленно), то лучший способ поддержки сессии — это передача её номера через параметры запроса. Единственной универсальной альтернативой является использование cookies. Но, наверное, не стоит долго объяснять разработчику, насколько удобно бывает работать с одного клиента в нескольких сессиях одновременно (что c cookies невозможно), да и запись номера сессии в основной access log может здорово пригодиться при отладке.
Ну и наконец, иметь возможность наследовать разом все параметры от запроса к запросу (сколько бы их ни было на входе) — это просто здорово. Например, на экранах поиска с многочисленными фильтрами.
Решение
В Eludia HTML-код генерируется не напрямую (хотя на кайний случай такая возможность имеется), а при помощи вызова API-процедур draw_form, draw_table и draw_tree. При этом все ссылки задаются в качестве опций href в описаниях кнопок, ячеек и т. п.
Каждое такое описание обрабатывается дважды: сначала в общей части ядра уточняются все опции, а потом при помощи выбранного $_SKIN'а генерируется собственно HTML. Например, описание кнопки сначала уточняется процедурой draw_toolbar_button, которая затем передаёт свой исправленный списко аргументов методу $_SKIN -> draw_toolbar_button.
Так вот, для каждого описания с опцией href вызывается процедура check_href. Она приписывает с исходной ссылке текущий sid, заменяет имеющийся salt на новое случайное значение и производит ещё ряд преобразований.
Наследование параметров
check_href может работать в 2 режимах в зависимости от типа параметра. Если на входе строка,
href => '/?type=users',
то производятся только преобразования, необходимые для работы системы (наследование номера сессии, установка номера экрана и т. п.). Если же передан хэш,
href => {type => 'users'},
то ссылка формируется из текущего %_REQUEST, при этом переопределяются только параметры, явно упомянутые в качестве опций (как в данном случае type).
В зависимости от имени, каждый параметр запроса может:
- наследоваться всегда (в 1-м и 2-м режиме);
- участвовать в автонаследовании по запросу (только во 2-м режиме);
- не наследоваться никогда.
Как отмечалось выше, параметр sid (номер сессии) наследуется всегда, параметры номера экрана __last_query_string и __last_scrollable_table_row не наследуются, но обязательно пересчитываются и устанавливаются. Кроме того, всегда наследуются значения параметров select (признак того, что текущее окно — не основное окно приложения, а расширяемый справочник для списка выбора) и __tree (признак того, что текущее окно вызвано во фрейме explorer like-интерфейса).
Не наследуются никогда параметры salt (случайный параметр для предотвращения кэширования), error (текущее сообщение об ошибке) и password (пароль при входе).
В отношении прочих параметров действует простое правило: если имя начинается с символа '_', параметр не наследуется никогда, иначе — участвует в автонследовании по запросу.
Наследование при перенаправлении
Функция redirect обрабытывает свои аргументы той же самой check_href, что HTML-функции, соответственно, при перенаправлении клиента параметры изменяются по вышеописанным правилам.
Отметим, что redirect ({action => ""}) вызывается автоматически по завершении обработки действия, если только ранее не был установлен $_REQUEST {__response_sent} (например, в результате явного вызова redirect ({}), в частности, esc).
Скрытые поля форм
Наследование параметров запроса реализовано не только путём модификации URL (собственно "URL rewriting"), но и через поля форм типа hidden.
Однако в данном случае имеется особенность: для форм, порождаемых draw_table, параметры с именами, не начинающимися с '_', наследуются автоматически, а для draw_form их имена необходимо упоминать явно в опции keep_params.
JavaScript
Обработка опций href не сводится исключительно к манипуляциям с параметрами. В ряде случаев, обычная http-ссылка может превратиться в javaScript.
Запрос подтверждения
Обработчик действия часто бывает весьма полезно предварить предупреждающим вопросом. Самая удобная программная реализация такого вопроса — window.confirm на ссылке, ведущей с экранной кнопки. Однако писать каждый раз соответствующий javaScript-код было бы крайне нежелательно. В Eludia достаточно указать опцию confirm
... href => {action => 'delete'}, confirm => 'Вы уверены ?!!', ...
и гиперссылка для данной кнопки будет обёрнута в нужный javaScript.
Диалоги
По умолчанию http-ссылки открываются в текущем окне. Если требуется указать другое окно (или фрейм), то соответствующее имя следует указать в опции target (наиболее частый пример: скрытый фрейм invisible для обработки действия). Однако есть особый случай, когда в момент активации ссылки целевое окно ещё не существует. То есть страница должна быть окурыта во всплывающем окне.
В Eludia предусмотрен такой приём для показа модальных диалогов: вы оформляете ссылку (опцию href), как обычно, и указываете рядом опцию dialog:
... href => "/?type=dmnds_pays&action=create&id_dmnd=$i->{id} ... ", dialog => { title => 'Платёж', after => '_reload (result)', options => { width => 600, height => 200, }, } ...
Необрабытываемые ссылки
Слишком много автоматики — это не очень хорошо. И для check_href предусмотрен ряд случаев, когда содержимое href передаётся в выходной HTML без обработки:
- почтовые ссылки ("mailto:...");
- ссылки-скрипты ("javaScript:...");
- ссылки с закладкой ("...#").