Экранирование текста, встраиваемого в HTML и URL

ekranirovanie teksta vstraivaemogo v html i url Сценарии на стороне сервера

Этот сценарий следит за форматированием текста фрагментов программного кода на каждом языке с помощью вспомогательной функции cgi.escape. Эта стандартная утилита Python автоматически преобразует специальные символы HTML в экранированные последовательности HTML, чтобы броузеры не интерпретировали их как операторы HTML. Технически функция cgi.escape преобразует символы в экранированные последовательности в соответствии со стандартными соглашениями HTML: <, > и & превращаются в &lt; , &gt; и &amp; . Если передать ей значение True во втором аргументе, то и символ двойной кавычки (") будет преобразован в &quot;.

Например, оператор << сдвига влево в программном коде на языке C++ транслируется в последовательность &lt;&lt; — пару экранированных последовательностей языка разметки HTML. Поскольку каждый выводимый фрагмент кода в итоге встраивается в поток вывода разметки HTML ответа, необходимо преобразовывать все содержащиеся в нем специальные символы HTML. Инструменты анализа HTML (в том числе стандартный модуль Python html.parser, который будет рассматриваться в главе 19) транслируют экранированные последовательности обратно в исходные символы при отображении страницы.

В более широком смысле, поскольку модель CGI основывается на концепции передачи форматированных строк через Сеть, операция экранирования специальных символов встречается повсеместно. Сценариям CGI почти всегда для надежности приходится экранировать текст, генерируемый для включения в ответ. Например, если возвращается произвольный текст, введенный пользователем или полученный из источника данных на сервере, обычно нельзя гарантировать, что в нем не будет специальных символов HTML, поэтому на всякий случай нужно его экранировать.

В последующих примерах мы также увидим, что символы, вставляемые в строки адресов URL, генерируемых нашими сценариями, тоже могут потребовать экранирования. Например, литеральный символ & в адресе URL является специальным и должен быть экранирован, если появляется в тексте, вставляемом в URL. Однако синтаксис URL резервирует другие специальные символы в сравнении с синтаксисом HTML, а потому должны использоваться другие инструменты и соглашения по экранированию. Как мы увидим далее в этой главе, функция cgi.escape реализует экранирование символов в разметке HTML, а urllib.parse.quote (и родственные ей) экранируют символы в строках URL.

Ими та ция дан ных, вво ди мых через форму

Здесь снова ввод данных из формы имитируется (моделируется) как для целей отладки, так и для ответа на запрос всех языков в списке. Если глобальная переменная debugme в сценарии получит значение True, к примеру, то сценарий создаст словарь, взаимозаменяемый с результатом вызова cgi.FieldStorage — его ключ «языка» будет ссылаться на экземпляр подставного класса dummy. Этот класс, в свою очередь, создаст объект с таким же интерфейсом, как в содержимом результата вызова функции cgi.FieldStorage, — он сконструирует объект, атрибут value которого будет установлен равным переданной строке.

В итоге мы получаем возможность тестировать этот сценарий, запуская его из командной строки: сгенерированный словарь заставит сценарий считать, что он был вызван броузером через Сеть. Аналогично, если названием запрашиваемого языка является «All», сценарий обойдет все записи в таблице языков, создавая из них искусственный словарь (как если бы пользователь поочередно запросил все языки).

Это позволяет повторно использовать имеющуюся логику функции showHello и отобразить программный код на всех языках на одной странице. Как всегда в Python, мы программируем интерфейсы объектов и протоколы, а не конкретные типы данных. Функция showHello успешно обработает любой объект, откликающийся на обращение form[‘language’].value. Теперь снова вернемся к взаимодействию с этой программой. Обратите внимание, что того же результата можно было бы добиться, использовав в функции showHello аргумент со значением по умолчанию, правда при этом пришлось бы предусматривать обработку специального случая.

Теперь снова вернемся к взаимодействию с этой программой. Если выбрать конкретный язык, наш сценарий CGI сгенерирует в ответ разметку HTML, как показано ниже (а также необходимые заголовок «Con- tent-type» и пустую строку). Воспользуйтесь пунктом меню View Source (Исходный код страницы или Просмотр HTML-кода) в своем броузере, чтобы убедиться в этом:

<TITLE>Languages</TITLE>

<H1>Syntax</H1><HR>

<H3>Scheme</H3><P><PRE>

(display "Hello World") (newline)

</PRE></P><BR>

<HR>

Программный код заключен в тег <PRE>, который определяет предварительно отформатированный текст (броузер не станет форматировать его как абзац обычного текста). Этот ответ показывает, что мы получаем при выборе языка программирования Scheme. На рис. 15.22 показана страница, отправленная сценарием после выбора в раскрывающемся списке языка «Python» (который в этом издании и на ближайшее будущее, конечно же, соответствует версии Python 3.X).

Рис. 15.22. Страница ответа, созданная сценарием languages.py

 

Наш сценарий принимает также значение «All» в качестве имени языка и интерпретирует его как запрос на вывод синтаксических конструкций для всех известных ему языков. Например, ниже приводится разметка HTML, которая генерируется, если установить значение True в глобальной переменной debugme и запустить сценарий из командной строки с одним аргументом All. Это та же разметка, которую получит броузер клиента в ответ на выбор пункта «All»:

C:\\PP4E\Internet\Web\cgi-bin> python languages.py All

Content-type: text/html

<TITLE>Languages</TITLE>

<H1>Syntax</H1><HR>

<H3>C</H3><P><PRE>

printf("Hello World\n");

</PRE></P><BR>

<H3>Java</H3><P><PRE>

System.out.println("Hello World");

</PRE></P><BR>

<H3>C++</H3><P><PRE>

cout &lt;&lt; "Hello World" &lt;&lt; endl;

</PRE></P><BR>

<H3>Perl</H3><P><PRE>

print "Hello World\n";

</PRE></P><BR>

<H3>Fortran</H3><P><PRE>

print *, ‘Hello World’

</PRE></P><BR>

<H3>Basic</H3><P><PRE>

10 PRINT "Hello World"

</PRE></P><BR>

<H3>Scheme</H3><P><PRE>

(display "Hello World") (newline)

</PRE></P><BR>

<H3>SmallTalk</H3><P><PRE>

‘Hello World’ print.

</PRE></P><BR>

<H3>Python</H3><P><PRE>

print(‘Hello World’)

</PRE></P><BR>

<H3>Pascal</H3><P><PRE>

WriteLn(‘Hello World’);

</PRE></P><BR>

<H3>Tcl</H3><P><PRE>

puts "Hello World"

</PRE></P><BR>

<H3>Python2</H3><P><PRE>

print ‘Hello World’

</PRE></P><BR>

<HR>

Все языки представлены здесь по одной и той же схеме — функция showHello вызывается для каждой записи в таблице вместе с моделируемым объектом формы. Обратите внимание на то, как экранируется программный код на языке C++ для встраивания в поток HTML — это результат вызова функции cgi.escape. При отображении страницы веб-броузер преобразует экранированные последовательности &lt; в символы <. На рис. 15.23 показано, как выглядит в броузере страница ответа на выбор пункта «All» — порядок следования языков программирования псевдослучайный, потому что для их хранения используется словарь, а не последовательность.

Рис. 15.23. Страница, возвращаемая в ответ на выбор пункта «All»

 

Проверка отсутствующих или недопустимых данных

До сих пор мы запускали этот сценарий CGI, выбирая название языка из раскрывающегося списка на главной странице HTML. В данном контексте можно быть вполне уверенным, что сценарий получит допустимые входные данные. Однако обратите внимание, что ничто не мешает пользователю передать сценарию CGI название языка в конце адреса URL, в виде явно заданного параметра, не используя форму страницы HTML. Например, следующий адрес URL, введенный в адресной строке броузера или переданный с помощью модуля urllib.request:

http://localhost/cgibin/languages.py?language=Python

дает ту же страницу ответа «Python», которая изображена на рис. 15.22. Однако, поскольку пользователь всегда может обойти файл HTML и использовать явный адрес URL, существует возможность вызвать сценарий с названием неизвестного ему языка программирования, которого нет в раскрывающемся списке HTML (и, соответственно, нет в таблице нашего сценария). На самом деле сценарий можно запустить вообще без параметра language (или без значения в параметре), если явно ввести адрес URL в адресную строку броузера или отправить его из другого сценария с помощью urllib.request, как было показано ранее в этой главе. Корректные запросы будут обработаны нормально:

>>> from urllib.request import urlopen

>>> request = http://localhost/cgibin/languages.py?language=Python

>>> reply = urlopen(request).read()

>>> print(reply.decode())

<TITLE>Languages</TITLE>

<H1>Syntax</H1><HR>

<H3>Python</H3><P><PRE>

print(‘Hello World’)

</PRE></P><BR>

<HR>

При этом для надежности сценарий явно проверяет оба случая, ведущие к ошибке, как должны в целом делать все сценарии CGI. Например, ниже приводится разметка HTML, сгенерированная в ответ на запрос фиктивного языка GuiDO (и снова вы можете увидеть код разметки, выбрав в броузере пункт меню View Source (Исходный код страницы или Просмотр HTML-кода) после того, как введете адрес URL вручную):

>>> request = http://localhost/cgi-bin/languages.py?language=GuiDO’

>>> reply = urlopen(request).read()

>>> print(reply.decode())

<TITLE>Languages</TITLE>

<H1>Syntax</H1><HR>

<H3>GuiDO</H3><P><PRE>

SorryI don’t know that language

</PRE></P><BR>

<HR>

Если сценарий не получает во входных данных названия языка, по умолчанию обрабатывается вариант «All» (то же самое относится к случаю, когда сценарий получает в URL параметр ?language= без названия языка):

>>> reply = urlopen(‘http://localhost/cgi-bin/languages.py’).read()

>>> print(reply.decode())

<TITLE>Languages</TITLE>

<H1>Syntax</H1><HR>

<H3>C</H3><P><PRE>

printf("Hello World\n");

</PRE></P><BR>

<H3>Java</H3><P><PRE>

System.out.println("Hello World");

</PRE></P><BR>

<H3>C++</H3><P><PRE>

cout &lt;&lt; "Hello World" &lt;&lt; endl;

</PRE></P><BR>

…часть строк опущена…

Если не отслеживать такие случаи, то весьма возможно, что сценарий завершился бы в результате исключения Python, а пользователь получил бы по большей части бесполезную и незавершенную страницу или страницу по умолчанию с сообщением об ошибке (здесь мы не связали потоки вывода stderr и stdout, поэтому сообщение Python об ошибке не выводилось бы). Наглядно это показано на рис. 15.24, где представлена страница, генерируемая при вызове такого явно заданного URL:

http://localhost/cgibin/languages.py?language=COBOL

Для проверки такого варианта ошибки в раскрывающийся список включено название «Other», при выборе которого создается аналогичная ответная страница ошибки. Добавление в таблицу сценария программного кода программы «Hello World» на языке COBOL (и на других языках, которые вы могли бы вспомнить из опыта своей работы) оставляется читателю в качестве упражнения.

Рис. 15.24. Страница ответа для неизвестного языка

 

Дополнительные примеры запуска сценария languages.py вы найдете в конце главы 13. Там мы использовали его для тестирования вызова сценария из клиентских сценариев, использующих низкоуровневый интерфейс к протоколу HTTP и модуль urllib, но теперь у вас должно быть еще и представление о том, как вызываются сценарии на сервере.

Использованная литература:

Марк Лутц — Программирование на Python, 4-е издание, II том, 2011

Каталог сайтов Всего.ру
Оцените статью
Секреты программирования
Добавить комментарий